20 Sep 2026 · 7:44 AM MTUpdated just now
What is being built, on purpose

Construction

In flight

2 items

working on these now
C-76renderer defectClaude

NO MEASURED EDGE AGAINST THE SPREAD. Sean stakes $2,000 on 2026-10-10

Why
Sean, 2026-09-20: 'I'm betting $2K at least on 10/10/26, so I need your model to be working then.' Measured before anything else was built on top of it. playerweek/teamstate.predict -- the direct team-strength margin model, a Kalman filter that predicts each week BEFORE assimilating it, and which never reads a market column (football_games selects explicit football-only columns) -- run over 2019-2026 and scored against the closing spread from games_raw.
Detail
ATS 0.4940 on n=1,844, 95% interval [0.4712, 0.5168]. Break-even at -110 is 0.5238, which the interval EXCLUDES. Straight-up the model picks 63.22% against the closing favourite's 65.72% -- it does not even pick winners as well as simply backing the favourite, which is itself 68.31% over 833 games 2023-2026 (measured independently, and it matches Fable's 68.32%). NOT A TUNING PROBLEM. All six settings in teamstate.FAMILY were run; every one of them has a 95% UPPER bound below break-even: q0.25-r0.65 0.4946 hi 0.5174 q0.25-r0.85 0.4892 hi 0.5120 q1-r0.65 0.4935 hi 0.5163 q1-r0.85 0.4940 hi 0.5169 q4-r0.65 0.4930 hi 0.5158 q4-r0.85 0.4978 hi 0.5207 Not one season since 2019 clears break-even either; the best is 2024 at 0.5299 (n=268), inside noise. WHAT THIS DOES AND DOES NOT SAY. It measures the model that EXISTS AND RUNS. It does not measure the play-grain simulator (research/playgrain/, gated, not wired in, whose README reports 64.32% SU and margin CRPS 7.41 against a climatological 8.16 but no ATS figure), and it does not measure totals, props or alt lines, where an edge could live and is unmeasured. It also does not say the project cannot find an edge -- it says there is no evidence of one today, twenty days out, and that a 0.494 hit rate at -110 is an expected return of about -5.7% per wager. WHAT WOULD CHANGE IT, and the only honest route in twenty days: C-66's CLV ledger. Win-loss over four weeks cannot show an edge at any plausible true rate; closing-line value can, in about 62 decisions at half a point. That needs C-04 (the EARLY line captured Monday-Wednesday, the number he actually faces) and proving.jsonl, which does not exist -- so no forward betting week has ever been opened and none of the 3,129 prop-probability rows is settled. Those two are now the critical path to 10/10.
C-10renderer defectClaude

Verify a generation flows end to end

Why
Every piece is now scheduled but no NEW generation has been produced and shipped yet. Until one is, this is wiring, not a working loop.
Detail
forecast-refresh is running its first cycle. Watch for a new generation_id on Maintenance, then confirm deploy-generation ships it and the customer page swaps without a reload.

Queued

46 items

C-75next upClaude

inj_raw has no clock, and week 1 dropped 19 of its 21 designations

Why
Two smaller defects found underneath C-71. Both bear on any honest as-of injury feature (Fable's A-01) and on grading.
Detail
(1) NO CLOCK: inj_raw.date_modified is NULL on every 2026 row, so there is no way to ask what the injury report said at a lock -- the exact thing a frozen forecast needs. 2025 and earlier carry it. (2) WEEK 1 AGAIN: 21 of week 1's skill-position inj_raw rows carry a game status and ALL 21 join cleanly to player_week, yet player_week shows only 2 tagged. Week 2 is clean (24 -> 24) and 2025 is clean (809 -> 828). Week 1 was built by a different path, which is now the fourth distinct defect traced to that boundary (see C-46). (3) COVERAGE: even where it works, only 5-6% of rostered skill player-weeks carry a designation while ~50% are DNP -- so the injury report explains a minority of who sits, and roster/depth status explains most of it.
C-70next upClaude

Gemini mice: the path works, and plan mode is NOT the containment

Why
Sean, 2026-09-20: '100 Gemini mice is basically free.' Probed rather than assumed, because a bar reported without a probe is usually the seat's bar and not the world's.
Detail
WHAT WORKS. The `gemini` CLI (v0.60.0, oauth-personal) is DEAD: 'This client is no longer supported for Gemini Code Assist for individuals... migrate to Antigravity.' But Antigravity is already on this machine and live -- ~/.local/bin/agy, 183 MB, and ~/.gemini/antigravity-cli/cli.log was calling loadCodeAssist six minutes before I looked. `agy` is a full agentic CLI: --print headless, --mode plan, --model, --effort (low|medium|high), --output-format json|stream-json, --json-schema for structured output, --add-dir, MCP support. Models offered: Gemini 3.8/3.7/3.6 Flash at three effort levels, Gemini 3.1 Pro, and -- billed to Google -- Claude Sonnet 4.6 and Opus 4.6. Verified it does real work: read bin/freeze.sh, counted 111 lines, quoted the exact sentence asked for. WHAT DOES NOT WORK, AND THIS IS THE FINDING. --mode plan is NOT a containment boundary and neither is --sandbox. With --dangerously-skip-permissions set, BOTH wrote a canary file to /tmp that they had been told only to plan. I had assumed plan mode enforced the mice lane's own rule (it may not edit playerweek/) at the tool layer; it does not. The real boundary is the headless DEFAULT: with no skip flag, a write is refused with 'the write_file permission that headless mode cannot prompt for, so it was auto-denied' and no file appears. Deny-by-default is solid; the flags are decoration. SO THE RECIPE IS: agy --print, NO --dangerously-skip-permissions, and a permissions.allow list naming only the read-only commands a mouse needs. One thing unresolved: a project-local .gemini/settings.json in the repo was NOT honoured (the allowed read command was still auto-denied), so the allow-list location has to be found before this is scheduled -- his global ~/.gemini/antigravity-cli/settings.json also drives Gemini.app and should not be edited blind. Note also that trustedWorkspaces there is /Users/seano, i.e. the whole home directory, which is why a skip-permissions run roams freely. That is C-61's argument exactly: contain with filesystem permissions, not with a flag.
C-67next upClaude

Every research lane is designed and none of them is loaded

Why
Sean, 2026-09-20: 'how do we use the Gemini mice? Or do we save Gemini for deep research, and we use haiku model agents as the mice?' Measured before answering: launchctl holds fourteen com.playerweek.* lane jobs -- mice, referee, rank, fable, and ten lane.* -- and NOT ONE IS LOADED. 259 crumbs sit in research/scientist/crumbs.jsonl unconsumed. Fable's review says the same thing from the staffing page's own words: the automated research jobs are disabled and the 46-job model is a design, not a running system.
Detail
So the model question is downstream of a bigger one. Picking a model for a lane that does not run is the harness work CLAUDE.md names. Load the lanes that earn their place -- per Fable's seven loops (C-48's companion) that is capture, release, grader, builder, adversary, scout, director -- and let the yield ledger add the rest. The mice belong to the mechanical audit lane and are the cheapest of them.
C-66next upClaude

Betting: CLV is the only instrument that can show an edge in the time available

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT., and it does not soften our own rule. Win-loss over four weeks cannot show an edge at any plausible true rate; closing-line value can, in about 62 decisions at half a point.
Detail
proving.py is the ledger, it is empty, and it cannot be backfilled -- so it starts the week the layered scorecard (C-48) says the GAME layer is at least at parity with the market. Until the CLV interval excludes zero the rule stakes $0. Supersedes the framing of C-29: the question is not what to stake, it is when the ledger may open.
C-65next upClaude

Shadow joint generator on the 384 coherent team-weeks

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. The world-model compiler is the right north star for joint questions -- game scripts, correlated lineups, betting tails -- and is a multi-season build that the handbook itself warns can lose to a simpler model.
Detail
For this season, stand up the small honest version: semi-Markov possession with a score/clock response on top of sim.py, SHADOW ONLY, scored on energy score against the terminal-state Gaussian. The experiment is already designed in §34. It takes one of the two challenger slots from C-60. Keep the direct model as champion for means; expect the generator to win first on joint tails, as §23 predicts. DEFER to the offseason: opposing-policy self-play, the seven-layer factor compiler, the eight hidden-state sensor programs.
C-64next upClaude

Move capture off the laptop -- a sleeping Mac loses observations forever

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT.: 'What cannot wait is capture. Received-time revisions, quiet players, source silence, failed fetches, full question-and-answer context, quoted market terms. No offseason can rebuild those. If the Mac sleeps, observations are lost forever, so capture is the one job I would move off the laptop.'
Detail
This is not theoretical and it already cost us: week 2's freeze was lost because the Mac was asleep at the noon lock (C-51). The catch-up job now recovers a LATE freeze, but it cannot recover an observation that was never made -- a revision that appeared and was superseded while the machine slept is gone. Scheduling a wake needs root; pmset schedule was refused 2026-09-20 03:55 running as seano. Candidates: a Cloudflare Worker on the capture endpoints, or a cheap always-on host.
C-63next upClaude

The handbook and the runtime disagree about which jobs exist

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Handbook §14 names nine institute.* jobs; launchctl shows eleven playerweek jobs of which only institute.forecast-refresh is loaded under that name. Either they were renamed or they are unloaded, and the document does not know.
Detail
Also from the staffing page, in its own words: the automated research jobs are disabled and bin/scientist.sh is hard-coded to Opus -- the 46-job model is a design, not a running system. Make the handbook read the runtime rather than assert it, the same way the Maintenance tab is measured and the construction board is asserted.
C-62next upClaude

87 Python scripts at the repo root, and one that mutates its own findings

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. 87 scripts sit at the repo root, first committed 09-19 19:58 as 'Monitor rebuild output'. One of them, fix_fake_papers.py, REWRITES Gemini findings JSON in place under research/gemini-deep/ to replace placeholder paper entries.
Detail
That is the discovery lane mutating its own findings, which the handbook's no-mutation rule forbids -- findings are immutable and a correction is a new version. Quarantine the 87 into their own directory, and add a deterministic citation-resolves check to the Scout lane so a placeholder citation fails on arrival instead of being edited away afterwards.
C-61next upClaude

Remove authority from workers instead of proving it is safe

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Three control-plane repair cycles have failed, each broken by independent review, and handbook §35 concludes it needs a new trusted launcher. Fable's reading: the design is trying to make untrusted workers safely HOLD authority, and the fix is to take it away.
Detail
Workers run in a separate checkout with filesystem permissions that cannot touch main, model-releases/, forecast-refresh/ or the scorecard tree. They emit a patch and a receipt; a deterministic gate script and the root session promote. One SQLite single-writer for leases. The incident-report threat model (evaluator capture, evidence spoofing) only bites when agents can write the evidence -- take the write away and §35's trusted launcher is not blocking any forecast gain, so by our own rule it should not be started. RECORDED DECISION: defer the trusted launcher.
C-60next upClaude

Cap simultaneous prospective challengers at two

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. The binding constraint is PROSPECTIVE WEEKS, not tokens. At n~11,000 player-weeks the interval on an RMSE difference is about +/-0.009; at team grain n=545 could not certify a planted 0.35 correlation. Fifteen weeks remain, roughly 5,000 relevant player-weeks -- Fable's estimate is that this season can prospectively confirm about THREE promoted changes at player grain and NONE at team grain.
Detail
Against that capacity the staffing page's Tuesday quota alone (one falsifiable idea per desk per week x 9 desks x 15 weeks) produces 135 candidates, before the daily Gemini packets. 'That is a forking-paths machine even with pre-registration.' Adopt: one statistical family per run (we have this rule), a standing queue ranked by expected layer-score movement, and a hard cap of TWO simultaneous prospective challengers.
C-59next upClaude

Q-arm: the four variables that survive are structured, not language

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Fable rates Q the weakest of the three reports and names why: S2 scores, VR usage, psychological safety and locker-room audits cannot be observed from outside a building, 'leadership explains 68.2% of variance' is a fixed-effects decomposition close to team identity, 'chemistry raises win probability 20-30%' traces to a blog, and micro-expression reading is contested science with no as-of data path.
Detail
What survives is testable now: (1) travel distance and time zones INTERACTED WITH QUARTERBACK AGE; (2) talent portability -- a new-team, new-scheme discount that varies by position; (3) pre-snap operational penalties as a discipline proxy; (4) coach, coordinator and GM continuity. Item (2) is C-34 and C-42's problem stated as a model term rather than a windowing bug, and is the first one to test because we have a live case in Wan'Dale Robinson.
C-58next upClaude

B-arm: the line of scrimmage is the transaction cost

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Pass-block and pass-rush win rates gate what can be called, and DPI is real yield that box scores record as zero. Neither is anywhere in our features.
Detail
Feeds C-52's opponent adjustment (trench win rates are one of the four pre-registered S families Fable names: opponent, trench, weather, travel-by-age).
C-57next upClaude

B-arm: play-caller effects with partial pooling, judged on play calls

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. The B arm's five surviving features, all as HIERARCHICAL play-caller effects with a transport test across team, quarterback and coordinator changes: neutral-script pass rate over expected; leverage-weighted opportunity (red zone, third down, two-minute); route participation and targets per route run; personnel grouping rates; fourth-down aggressiveness against an optimal-policy model.
Detail
Two admission rules travel with it. First, separate PLAY-CALLER INTENT from QUARTERBACK EXECUTION -- a checkdown is not a conservative call. Second, routes are not resting orders: decoy and clear-out routes never want the ball, which is the caveat on TPRR (C-37). Judged first on held-out PLAY CALLS, not on points. The transport test is the same defect as C-34 in another form.
C-56next upClaude

Confirm nflverse as-of availability and latency, field by field

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT.: 'confirm each nflverse field is available as-of, in season, at the latency you need. I believe recent-season participation data arrives late. I did not verify that.' A feature that arrives after kickoff is not a feature.
Detail
Unverified by Fable and unverified by us. It gates C-55, C-53 and every B-arm feature in C-57 -- all of which assume participation data is readable before a lock.
C-55next upClaude

Audit every participation field for post-snap charting

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT., quoting the B arm's own self-critique: 'defense_coverage_type is charted POST-SNAP. Using it as a pre-snap feature is leakage. Same audit for every participation field.'
Detail
A feature charted after the snap cannot be in a pre-kickoff forecast, and nothing in the project currently asserts that field-by-field. knowability.py is the right home: classify each participation column the way it already classifies POST_KICKOFF outcomes, and make the classification fail the build rather than live in a comment.
C-54next upClaude

Gamebook participation labels, so the non-playing branch has true zeros

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. The hurdle model's second stage cannot be fit without them: a player who was active and took no snaps must be a measured zero, not an absence.
Detail
This is the same distinction lags.sql already makes with on_roster (a week on a roster but inactive is a measured zero; a week off any roster is NULL and drops out). Expand the labels for all started weeks so availability can use it too.
C-53next upClaude

Availability hurdle model -- the 155-to-1.18 gap

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. The largest measured lever in the project, and it is not points. Our own measurement: language added nothing to points (+0.002 RMSE, interval spans zero, n=11,283) and a real amount to availability (-0.0038 Brier, adjusted p 0.016) -- while the consensus knows who sits at an odds ratio of 155 against our model's 1.18.
Detail
Build it as a hurdle: P(active), then P(plays | active), then conditional workload. Inputs are injury designations, practice participation, inactives at T-90, and the typed claims the Q capture already produces. Score on log loss against carry-forward and consensus, in shadow. Requires C-54.
C-52next upClaude

1.91 -- shrink per-player passing rates toward positional priors

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. The S model's largest named defect and the one both the S report and Fable point at: the played-only gap to FantasyPros was +0.136 RMSE overall and +0.975 for QUARTERBACKS (PASSDOWN 09-11). Hierarchical shrinkage toward a positional prior is the named fix, and it is the honest version of the 'touchdown-rate extremity' idea the S report dressed up as an RSI oscillator.
Detail
We already do exactly this for the volume baseline's efficiency half (SHRINK_TOUCHES=40 in bin/build-serving.py, measured). This is the same move inside the model. Fable's gate: parity with consensus on non-quarterbacks with an interval that includes zero, AND the quarterback gap under +0.30.
C-50next upClaude

313 SCHEDULE_IDENTITY_CONFLICT rows in weeks 16-17

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Named as one of two repairs that sit in FRONT of grading. A row that cannot resolve its own game identity cannot be graded, and 313 of them sit in the playoff weeks.
Detail
Fable's other pre-grading repair was the freeze job's exit 1 -- fixed 2026-09-20 03:54, see C-51. This one is untouched. Find whether the conflict is the week-1-vs-week-2 key-system boundary in another costume (see [[playerweek-data-key-systems]]) or a genuine schedule revision.
C-49next upClaude

Scorecard: 11,240 rows, ZERO graded

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Measured 2026-09-20 07:40 UTC: 11,240 rows, 10,576 selected, 0 graded, 9,599 pending on unfinished events. The instrument that is meant to govern the project has never produced a number.
Detail
664 week-18 rows carry no eligible forecast. The first week-2 Sunday slate kicked at 17:00 UTC on 09-20, so that day was the first real grading day for v1. Grade week 2 and publish the layered result; until it grades, every promotion decision is being made blind.
C-48next upClaude

The layered scorecard is the one instrument that governs

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. Its first and largest recommendation: score each LAYER separately on the full issued population rather than scoring a fantasy total -- availability (log loss), team volume (plays, pass rate), opportunity share, efficiency, stat line (CRPS and interval coverage), game (Brier, score CRPS). Every layer against the same frozen baselines at the same cutoff: naive carry-forward, consensus/Yahoo, market, and v1.
Detail
The point is the failure it exposes: 'a correct fantasy total with a wrong workload story should show up as two errors.' We currently cannot tell those apart, which is exactly the Wan'Dale case (C-47) -- right-ish number, wrong volume story. Fable's rule to adopt with it: nothing is promoted, and no harness work is started, unless it names the layer score it will move. Depends on C-49 and C-50 being cleared first, and on the frozen baselines that bin/freeze.sh now writes (ours, yahoo, rotowire, fantasypros, blend, trailing-3, season-to-date).
C-47next upClaude

The naive usage layer is the common cause behind C-34, C-37 and C-38

Why
Sean, 2026-09-20: 'I think this relates to the previous notes where our naive model needs more work.' He is right and the link is specific. Both the BASELINE shown on the page and the stat-line model's own projection are built from the same three trailing usage numbers -- target share, carry share, snap share -- and every open S-arm finding is a defect in exactly those three. They are not separate backlog items; they are one underbuilt layer with three symptoms.
Detail
Traced live on 2026-09-20 against two players on Sean's roster. WAN'DALE ROBINSON: target_share_l3 32.7% against a real Tennessee share of 18.8% (6 targets of 32), because the window averages a 14-target Giants game into a Titan (C-34). Share is the ONLY usage signal we carry, and it is a ratio whose denominator changes team; routes run / TPRR (C-37) is the rate that would not, and we have none. CAM SKATTEBO: carry_share_l3 48.6% off ONE game in which the Giants ran 37 times, their heaviest of the last five (37,32,31,30,29). His projection falls from 18.0 to 11.2 touches because the model regresses team rush volume -- correctly -- but nothing enforces that a team's backs sum to its rushes (C-38), so the regression is applied per player rather than to the team's pool. C-34, C-37 and C-38 should be planned together as one piece of work on the usage layer, not picked off one at a time. HARNESS NOTE for whoever does it: playerweek/backtest.Harness is the right instrument (week-by-week, leakage guard) but its features() returns bare player_week columns; playermodel.project needs rows built through arms.rows_for or share.feature_sql or share.apply raises rather than imputing 67 of 78 features. A playermodel.fit on this machine measured 882s, so an A/B over two feature sets is ~30 minutes of fitting before any projection runs.
C-44next upClaude

One source for Yahoo's number per player

Why
site_player.yahoo_projection is now populated, but the page reads Yahoo out of the generation's provider_comparisons block. Both come from the same rows, so they agree today by construction -- and that is exactly the arrangement that let floor_10 be correct in the table and absent from the page.
Detail
Make site_player.yahoo_projection the published source and have apply-serving write it onto the row, leaving site_comparison for the review table only. Low risk, but it moves where a rendered number comes from, so it wants its own change rather than riding along with a validator fix.
C-42next upClaude

A kicker's baseline follows the team slot, not the player

Why
special_baselines() keys on (week, position, team) because a payload row's id changes key systems at the week-1 boundary -- FantasyPros in week 1, gsis after -- while nfl_team is stable on both sides. The cost is that a kicker who changes teams inherits his new team's previous kicker's three games.
Detail
This is C-34's mirror: that one averages a player across a team change, this one averages a team across a player change. It may not even be wrong for a kicker -- offence quality plausibly dominates leg -- but it is unmeasured and it is not what the column claims to be. Measure both keyings against 2023-2025 kicker_scored before choosing; the honest answer may be to keep the team key and rename the column.
C-34next upClaude

Trailing usage must not average across a team change (baseline done, model open)

Why
Sean: 'why does the model think Wan'Dale is going to get so much more work than he got last week?' Traced: his target_share_l3 of 32.7% is the mean of 18.8% (6/32 as a TITAN, 2026 wk1) and 46.7% (14/30 as a GIANT, 2025 wk17). Half the evidence for the projection is a game for a different franchise. Applied to Tennessee's 32 attempts that is ~10.5 targets; his only Tennessee data point projects ~6, which is where Yahoo sits.
Detail
lags.sql windows on (player_id ORDER BY season, week) and never looks at the team column, so every trailing share, snap rate and volume feature blends across a move. It is worst early in a season, when one old game can be half the window. FIX: partition the l3 windows on (player_id, team), or carry a team_change flag and let the model discount pre-move rows. Either way it must be a declared choice, not silence. MEASURABLE: re-score week 1 with team-partitioned windows against the same actuals and compare MAE. The snapshot that produced the current forecast is retained, so the comparison is exact. This is the single clearest defect found so far and it is currently driving a live start/sit recommendation. MEASURED 2026-09-20, and the measurement is NOT a win: replaying 2019-2025, on the 772 player-weeks where a stint-reset window and the current one disagree, the stint window is better by 0.115 MAE (t = 1.19, closer on 52.6% of rows). Not significant; not claimed as an accuracy improvement. 4.88% of WR/RB/TE player-weeks have a window spanning more than one club. PARTLY DONE: the volume BASELINE (bin/build-serving.volume_baseline) now resets volume at a move while keeping efficiency career-wide, because the column is a description and a Titan's recent touches are not a Giant's -- Wan'Dale Robinson's week-2 baseline went 12.91 -> 7.74 and the projection went from reading 0.69 BELOW baseline to 4.48 ABOVE it, which is the anomaly made visible rather than masked. STILL OPEN: lags.sql itself still windows on player_id, so target_share_l3 (33% for Wan'Dale, half of it a Giants game) still feeds the MODEL. Changing that needs a refit and an out-of-sample evaluation of the full stat-line model, not of the baseline predictor alone. A THIRD FRAMING, 2026-09-20, from Fable's read of the Q arm: 'talent portability -- a new-team, new-scheme discount that varies by position' is this same problem stated as a MODEL TERM rather than as a windowing bug, and it is one of only four Q variables that survived review. See C-59. We have a live case to fit it on.
C-33next upClaude

The qualitative layer is not yet worth anything

Why
Measured, week 1, 706 players: S 5.091, SB 4.987, SBQ 4.990.
Detail
Adding B (decisions/judgement computed from structured data) buys 0.104 MAE. Adding Q (claims made in language) LOSES 0.003 -- SBQ is worse than SB. One week and n=706, so not conclusive, but it is the opposite of the assumption the world-model work rests on. Needs several weeks of the ablation (C-31) before any weight is put on the Q layer.
C-32next upClaude

Reconcile the arm path with the production model

Why
They are not variants of one model; the production number is not one of the arms.
Detail
Week 1 Cam Skattebo: arms give S 2.16-4.38, SB 4.17-4.22, SBQ 3.95-4.11 while production published 11.88 (actual 17.10). Wan'Dale: arms 12.52-15.25, production 11.88-ish. Two different code paths and two different fits, so an ablation of the arm path says nothing about the number on the page. Either the production model is registered as an arm set or the ablation is run on the production path -- until then SBQ attribution is measuring something Sean never sees.
C-31next upClaude

Run the SBQ ablation on the live forecast weeks

Why
Sean asked how a projection breaks down from SBQ. It cannot be answered: arm_forecast holds 2026 WEEK 1 ONLY.
Detail
The arm framework exists and works -- 43,215 rows, arms S / SB / SBQ, 706 players for week 1 -- and has not run since. Every current week's number has no attribution, so 'why do we differ from Yahoo on this player' has no mechanical answer. Run it per forecast week and archive it beside forecast_explained.
C-30next upClaude

Blend our projection with Yahoo before recommending a lineup

Why
Sean asked how we justify a start/sit against Yahoo. Tested on all 135 week-1 players where our number, Yahoo's and the result all exist: we CANNOT distinguish our model from Yahoo. MAE 6.686 vs 6.859, paired diff -0.173 with 95% CI [-0.526,+0.185] crossing zero, p=0.346; closer on 79/135, binomial p=0.058; correlation with actual 0.566 vs 0.554. ~1,184 player-weeks (~8.8 weeks) needed for 80% power on an effect that size.
Detail
THE ONE SIGNIFICANT RESULT: a 50/50 blend beats Yahoo alone (6.635 vs 6.859, p=0.036) and is no worse than ours alone (p=0.625). Best in-sample weight is ~0.7 ours, but that is fit on the evaluation data -- 50/50 is the honest default until there are enough weeks to fit a weight out of sample. IT CHANGES DECISIONS: week 2 start/sit reads Wan'Dale over Skattebo by 2.12 on our numbers and Skattebo by 5.73 on Yahoo's; the blend says Skattebo by 1.80. Recommending on our number alone is a 7.85-point bet against a source we have not shown we beat. Blocked on nothing. Needs a decision on whether the page shows the blend as the recommendation basis while keeping our raw number visible. Depends on C-25 (freeze ours-vs-Yahoo at cutoff) to keep measuring it honestly.
C-07queuedClaude

Build to the Codex operating model

Why
Sean, 19 Sept: the system Codex defined is the plan of record.
Detail
Read the nine-desk operating model in full, then sequence it. Not started -- the plumbing comes first.
C-08parkedClaude

Gemini system as challenger

Why
Run in parallel, measured against the POR. Adopted only where it demonstrably beats it.
Detail
research/playgrain/ -- play-grain warehouse, transition simulator, air-gapped loop. Built 18-19 Sept. Parked until the POR is moving.
C-14parkedClaude

Forecasted playoff seeding for weeks 15-17

Why
Sean, 2026-09-19: 'maybe they can be forecasted playoffs based on our projections, but that is low priority right now until the basics are working.' PARKED ON PURPOSE -- recorded so it is not lost, not so it gets built next.
Detail
The league feed records no playoff schedule and will not until standings finalise -- fp_league_matchup holds week 1 and nothing else. PLAYOFF_WEEKS in bin/enrich-generation.py is a declared setting sourced to Sean, which is correct for now. A forecast would project the remaining schedule to a seeding and name a likely opponent per playoff week, and it must ship as a forecast with its own scope -- never as a saved matchup. Blocked on nothing; deferred behind the basics.
C-19needs a producerClaude

AAR error attribution for 2026

Why
Sean asked for 'the AAR items that were dropped'. The 20%-from-Yahoo table on the Season Plan answers the substance -- ours, Yahoo's, the gap and the stat line behind it -- but the real AAR is richer: aar_attribution splits an error into variance, regime, volume, share and conversion.
Detail
BLOCKED, not skipped. aar_attribution holds 2024 and 2025 and nothing for 2026, and `pw aar --season 2026` refuses with EmptyComparison: no player-week carries a forecast from all seven legacy sources, so there is no common sample. Its own message is the right instinct -- 'an empty result is not agreement'. Unblocking it means filing 2026 forecasts from more than one source into aar_forecasts, which is institute work through the review path, not an edit here.
C-20queuedClaude

NFL playoff seed forecast

Why
The Playoffs page is entirely 'Seed forecast pending' / 'Awaiting simulation'.
Detail
DELIBERATELY NOT BUILT TONIGHT. The inputs exist -- spreads and moneylines for every remaining game now that odds_quote carries prices -- but a Monte Carlo playoff simulation is a MODEL, not arithmetic over saved products, and NFL seeding needs real tiebreaker logic (division winners seed 1-4). Guessing a seed puts a wrong number on a page whose whole claim is that it does not imply a forecast. standings_week also has no 2026 rows, so even the 'actual standings' half shows 2025 -- correctly labelled historical. Wants a decision on method before code.
C-21queuedClaude

A home for accuracy: AAR / statistics

Why
Sean, 2026-09-19: "hide the 'who as been right' tab for now, I'm not sure what that is for, if anything it should be part of some sort of AAR/statistics table somewhere, I don't need to see it on my team."
Detail
The tab is hidden, not deleted -- the comparison PRODUCT still ships in the payload (provider_comparisons, 14 weeks) and the renderer still exists, so nothing has to be rebuilt when it finds a home. My Team is for deciding this week's lineup; how right we have been is a different question asked at a different cadence. Pairs with C-19: the real AAR splits an error into variance, regime, volume, share and conversion, and that is the table this belongs beside. Until then the 20%-from-Yahoo view on the Season Plan carries the part that changes a lineup decision.
C-24next upClaude

Prove the model artifact is retained per release

Why
Reproducibility is claimed end to end but only the INPUTS are proven retained.
Detail
The refresh pins PW_REFRESH_MODEL_RELEASE=v1.0-20260916 with a manifest sha, and forecast_explained now records model_sha. Nobody has verified the artifact那 sha names is still on disk, or mirrored. Until that is checked, 're-run the same model on the same inputs' is an assumption. Check data/institute/model-releases, add it to bin/archive-retention.sh as a never-delete class.
C-25next upClaude

Freeze ours-vs-Yahoo at cutoff

Why
The only external benchmark we have, recomputed every run and never frozen.
Detail
yahoo_proj landing files survive, but the PAIRING at a point in time does not: when Yahoo moves its number the historical comparison is gone. Archive it beside forecast_explained so 'were we better than Yahoo, measured at the time we both committed' is answerable. PARTLY DONE 2026-09-20. bin/freeze.sh now writes a 'yahoo' source into bets at each lock, reading the last landed capture AT OR BEFORE that lock so a Wednesday bet cannot see Saturday's revision. Week 1 has none and cannot: every Yahoo capture postdates the 09-09 lock, and the projector refuses to backdate one. What remains is the comparison SURFACE, not the capture.
C-26next upClaude

Track the projected finish over time

Why
Recomputed every run, recorded nowhere.
Detail
site_league_rank is rebuilt on every producer run. Archiving it per generation would show whether a season-long claim drifts or holds -- the calibration question for anything we say beyond this week.
C-27next upClaude

Measure the payload on a phone

Why
13.8MB of JSON, and the app aborts its own fetch at 15 seconds.
Detail
Brotli takes it to 1.64MB on the wire so download is fine, but parse and memory on a mid-range phone are unmeasured. A cold headless browser took over 10s to first render; warm is 2s. Measure on a real device before assuming the margin is comfortable.
C-28next upClaude

Sweep the explain-itself prose off the other pages

Why
Sean: 'there is way too much explaining of my own platform to me, this needs to stop.'
Detail
Done on My Team. Betting, Model, Status and Playoffs still carry the same style -- roughly thirty sentences that narrate what a number is instead of showing it. Same principle: state the number, drop the sentence.
C-29next upClaude

Moneyline decision ledger

Why
The betting page's two headline tiles read 'Not published' and now they could be built.
Detail
Needs a prospective decision with the price obtained before kickoff and an official settlement. odds_quote now carries h2h prices with real juice (9,568 rows, hold verified), which was the blocker. What is still missing is the staking rule and the settlement join -- and the staking rule is Sean's call, not mine. SUPERSEDED IN FRAMING 2026-09-20 by C-66: the open question is not Sean's staking rule, it is WHEN the ledger may open. Win-loss over four weeks cannot show an edge at any plausible true rate; CLV can, in about 62 decisions at half a point. proving.py opens the week the layered scorecard says the game layer is at parity with the market, and stakes $0 until the CLV interval excludes zero.
C-35next upClaude

S-arm: purged walk-forward validation with an embargo

Why
The single best idea in the Gemini S research, and I do not know whether our fit does it.
Detail
NFL player-weeks are autocorrelated and a trailing feature spans several weeks, so a random split leaks the future into the training set and a model scores well in-sample then collapses. Standard practice in quant finance is PurgedGroupTimeSeriesSplit: successive training sets, group separation, and an EMBARGO gap sized to the longest lookback (ours is the 3-row l3 window, plus l8 features elsewhere). ACTION: read how fit_models.py splits, and if it is not purged walk-forward, make it so and re-measure. Until then every accuracy number we quote about our own model is suspect in the same way the 31-player sample was. CONFIRMED INDEPENDENTLY 2026-09-20 by Fable's build recommendation, which lists purged walk-forward with an embargo AT LEAST AS LONG AS THE LONGEST LOOKBACK FEATURE among the things to keep, and makes it the validation standard for every stage of the proposed model. Two sources, arrived at separately, now say the same thing; treat it as settled rather than as a proposal.
C-36next upClaude

S-arm: momentum oscillators measured HARMFUL -- do not rebuild

Why
Negative result, recorded so it is not proposed again.
Detail
The research's flagship indicators (Opportunity MACD, Efficiency RSI, OBO divergence, Bollinger squeezes) are built for thousands of ticks. An NFL player has ~17 games a season and a 4-game RSI has four observations. MEASURED: adding a MACD-style recent-vs-longer volume trend on top of volume x shrunk efficiency moved MAE from 4.143 to 4.333 (n=3,873, p<0.001) -- actively worse, not merely useless. The mean reversion those oscillators chase is already captured by shrinking efficiency toward a positional prior. TD-rate regression IS real and confirmed (top-decile 0.154/touch -> 0.065 next week vs a 0.045 league mean); it does not need an RSI to exploit. CONFIRMED INDEPENDENTLY 2026-09-20 by Fable, which reaches the same verdict from the other direction: the RSI-shaped idea (touchdown-rate extremity as a signal) is just regression to the mean, and hierarchical shrinkage toward a positional prior is the honest version of it -- which is C-52, the named fix for the quarterback gap. So the oscillator is not merely harmful, its one real insight already has a correct implementation waiting.
C-37next upClaude

S-arm: routes run (TPRR) is not in our data

Why
The most promising unexploited S-arm feature, and we cannot compute it.
Detail
Separating routes run (opportunity on the field) from targets (opportunity converted) gives target-per-route-run, which detects a role change before targets move. player_week has no routes column and nothing in the warehouse carries one -- only pff_rec_concept.slot_routes, which is partial. ACTION: find whether nflverse participation data or a PFF feed can supply routes run per player-week, then test TPRR as a volume feature against the current baseline on 2023-25. CONFIRMED INDEPENDENTLY 2026-09-20 by Fable, which lists targets per route run among the five surviving B-arm features (C-57) and attaches the caveat that matters: ROUTES ARE NOT RESTING ORDERS. Decoy and clear-out routes never want the ball, so TPRR is not a clean intent signal and must be modelled as a play-caller effect rather than read as a player's demand for targets.
C-38next upClaude

S-arm: team volume is zero-sum -- test the constraint

Why
A team throws a finite number of passes; our model projects each player independently.
Detail
Nothing enforces that projected target shares on one team sum to a sensible total, so an offense can be over- or under-allocated. This is testable directly: sum our projected targets by team-week and compare against actual team pass attempts. If the sums are systematically off, a normalisation step is worth more than any new feature. The research proposes 'cointegration' for this; the plain version is a constraint, and the plain version is testable this week. CONFIRMED INDEPENDENTLY 2026-09-20 by Fable, with a named alternative: 'vacated-share reallocation given an availability forecast does the cointegration job honestly.' That ties this directly to C-53 -- once the hurdle model can say who is out, the share model reallocates their volume rather than leaving the team's total unconstrained.
C-40next upClaude

S-arm: do not chase a Temporal Fusion Transformer

Why
Recorded as a decision, not an omission.
Detail
The research recommends a TFT. Our data is ~100-200k noisy player-weeks, where gradient boosting usually wins, and our own SBQ ablation says architecture is not where the gain is: adding the whole B class bought 0.104 MAE and the Q class bought -0.003. The measured wins so far are in FEATURES and CORRECTNESS -- the volume-route baseline (+0.205 MAE, p=1.7e-25) and the team-change window defect. Revisit only if a feature-level plateau is demonstrated. EXTENDED 2026-09-20 from Fable's build recommendation, which reaches the same verdict on the TFT and adds the rest of the list, all for the same reason -- about 5,500 relevant player-weeks a season is gradient-boosting and hierarchical-Bayes territory, and these are overfit machines at this N. DO NOT BUILD, and this is the record so nobody re-proposes them: TFT; GARCH; fractional differencing; HMM on weekly snaps; the HMM 'panic regime' (score, clock and win probability are OBSERVED, so condition on them directly); the mixture-of-experts gate; cointegration matrices; On-Balance Opportunity (the sign rule is arbitrary and 'mathematically guarantees a breakout' is folklore); the Bollinger squeeze ('low volatility is always followed by high volatility' has no football evidence). AND THE PREMISE ITSELF: the S report's claim that 'opponent, injuries and weather are already priced in to usage' is false on its face, and the B report's own critique says so -- which is why C-52's opponent, trench, weather and travel families are worth fitting at all.
In flight
2

Being built right now. If this is not what you want built, this is the page to say so on.

Queued
46

Agreed and waiting. The order is deliberate, not arbitrary.

Landed
24

Finished and verified. The list is at the foot of this page.

Plan of record
Codex

The nine-desk operating model. Gemini's system runs as a challenger, not the spine.

This board is asserted, not measured. It says what is deliberately being built and why. Maintenance reads the machine at render time and will contradict this page whenever the two disagree — which is exactly what it is for.

My Team — what needs fixing

Nine items. 0 open.

0 open

The operating rule: The page reads saved products. It does not fit a model, optimize a lineup, derive fantasy scoring, invent missing data, or turn a late observation into a pregame prediction. — v1 handbook §6

The page reads saved products, so every blank was a MISSING PRODUCT rather than a rendering bug -- and the products are now produced, on the data side, by the enricher that runs inside publish-data.sh. Two were genuine renderer defects against stated rules and are fixed in the site build. Measured against the live page, not asserted.

MT-01landed

season_plan is missing

Wrong
season_outlook is published: weeks 2-14, 1720.5 pts. Built in the enricher on the data side, which is a producer step -- the page still only reads it.
Screen
Plan value · Remaining points · Projected points rank
MT-02landed

recommendations is missing

Wrong
recommendations published on 17 weeks, 25 start/sit moves carrying their own arithmetic. Pregame weeks only; a finished week gets none.
Screen
This week's decisions
MT-03landed

comparisons is missing

Wrong
provider_comparisons available on all 14 fantasy weeks. The same-rules join is ours against Yahoo's own weekly capture; neither is an outcome.
Screen
Who has been right
MT-04landed

Yahoo column is empty for every player

Wrong
Yahoo column populated on every player and every TOTAL row across the 14 fantasy weeks. Weeks 15-18 have no Yahoo projection because the fantasy season ends at 14.
Screen
Head to head · YAHOO column
MT-05landed

Verify pregame renders as Upcoming, not an observed zero

Wrong
§9's dangerous half was already satisfied -- no invented zero -- and the label half now is: the week chip reads Upcoming for every pregame week. The separate 'Upcoming matchup' banner was removed at Sean's request; the chip and the scoreboard already said it.
Screen
ACTUAL · Score state
MT-06landed

Week 1 still shows the older capture

Wrong
NOT A BUG — verified. Week 1 is correctly labelled 'Older capture' because the fresh roster capture covers weeks 2-18 only; the generation declares this in its own issue log. The page is reporting a true roster-scope fact.
Screen
Weeks strip
MT-07landed

Weeks 15–18 must not imply a fantasy lineup

Wrong
The rail now reads 'NFL only' on weeks 15-18 and draws them dashed, so the horizon is disclosed even on mobile where the chip label is hidden. The data already distinguished them; the renderer did not.
Screen
Week selector
MT-08landed

Lineup alternative not published

Wrong
lineup_alternative published on the 3 actionable weeks. Scenarios are limited to that horizon on purpose -- publishing all 18 put the payload at 19.2 MB and the app aborts its fetch at 15s.
Screen
Your lineup
MT-09landed

Health feed 404s on the static site

Wrong
app.js polls /data/maintenance-health-live.json every 60s; it was never shipped, so the poll failed silently. Codex's envelope has max_age_seconds=180 — built for the loopback server that regenerates per request, so it cannot work on static hosting. Replaced with a liveness badge that reads the manifest on the same cadence.
Screen
liveness
MT-10landed

Week 1 K and DST had no actual, which withheld every TOTAL

Wrong
player_week holds no kicker and no defense rows at all. Those positions are scored in kicker_scored (initial and surname, 'E.Pineiro') and defense_scored (no name at all, only the team abbreviation), and Yahoo files a defense under its nickname alone. Two blank cells withheld the starter total for all ten players, because a sum is withheld unless every row has a value. Now: week 1 starters projection 139.81, actual 127.20, Yahoo 143.93, and every TOTAL row on all 18 weeks populates.
Screen
Week 1 · TOTAL row · Eddy Pineiro · Los Angeles Chargers

The machine, and what of it is actually running

11/38 running
CAPTURE · clocks that cannot be rewoundinstitute.capturehourly · articles, gamebooksraw qualitative evidenceinstitute.structuredevery 4h · public sourcesstructured archiveavailability · 15 minESPN inactivesgone if missednews-stream · 30 minbeat + podcast RSSland first, ingest laterexternal sourcesnflverse · ESPN · Yahooodds · weather · RSSSTORAGE · no mutation, identity by generationoperational storecaptures land without queueingbehind a measurementgeneration store · immutableevery forecast is an id + sha256replay is reading an old id, not refittingMODEL · v1.0, fixed artifacts, no silent refitmodel release v1.0manifest dcf95ac6…configured errors cannot refitinstitute.forecast-refreshevery 4h + Thu pre-kickoffsnapshot → forecast → validatevalidationfield parity · schema · rangesfield identity only, not accuracyweeks 2–18explicit core forecastsbye rows zero, no invented lineDELIVERY · what Sean actually opensdeploy-generation · 10 minships active → CloudflareBUILT 19 Sept · the missing halfplayerweek.pages.devthe customer sitepolls manifest every 60s, swaps liveinstitute.phone-syncevery 60sbounded status to the trackerinstitute.site-servicesupervised loopbackserves validated assetsOPERATIONS · proves the rest is trueinstitute.service-healthevery minuteavailability ≠ freshness ≠ correctnessinstitute.handbookevery 5 minregenerates the handbookinstitute.tracker-feedcontinuousseparates the three clockswatchdog · 15 minlast exit, lost firings,whether it WROTE anythingjanitor · 2× dailyback end AND the LIVE sitebuild vs served, stale numbersNINE DESKS · one chain of evidence · none edits the modelCommission & Portfoliowhat the machine is asked forWorld Model Labthe predictive coreFootball Intelligencethe sport itselfBehavior & QualitativeS / B / Q evidence classesData & Provenancewhere every number came fromMarkets & Portfolioprice, stake, exposureExperience Studiowhat Sean seesOperations & Learningthe loop that improves itForecast Accuracywere we rightpremise lanereferees ASSUMPTIONS, not findingsaddressed mail between desks19.5k mailbox + 24.8k addressed on the METR boarda desk that finds something off-topic has nowhere to put itMODEL LADDER · deterministic first, frontier lastdeterministicSQL and Python decideno model votes where code cancheap · narrow · numerousscreening and extractiondaily builders & criticsSonnet / Codexfour deliberate specialistsDeep Think · Opus · Sola frontier call matches one of four commissionsSHIPPING · the seat is not in the pathshipperdrains the refereed queue · gates walk-forwardALL PASS → commits itself · ANY FAIL → records the numberauto-revertindependent of the gate that let it indifferent season, different metric, computed by the refereethe seat · on demandescalations and ties onlya collaborator, not a queue
running · 11partial or in flight · 1not running · 2611 of 38 boxes green
Features from the v1 handbook and the nine-desk operating model. Colour is measured for every box backed by a launchd job — green means launchctl answered, not that somebody typed green. Boxes with no job yet are asserted from the handbook's own status matrix, where ACTIVE and INTEGRATED are green, UNDER CONSTRUCTION and IMPLEMENTED/INACTIVE are amber, and UNAVAILABLE is red.

Plan of record — every named component

From the v1 handbook and the nine-desk operating model, read in full rather than skimmed

8 of 93 exist

93 components across 10 groups. 8 are running, 8 are partial, 77 do not exist. Codex's own status words are kept rather than flattened, because UNAVAILABLE and NOT CERTIFIED mean different things and need different work. Where a component names a launchd job, the state is measured at render time; everything else is Codex's assertion carried over.

This is the gap between the machine that is described and the machine that runs. It is meant to be uncomfortable reading.

Living checklist

v1 handbook §17 — Codex's own states, carried over verbatim

6 of 13 live
ComponentStateWhat it is
Original v1 artifact auditACCEPTEDOriginal learned bytes reproduced under the frozen capsule and are serving.
Write-once v1 registry and guarded loaderACTIVERelease v1.0-20260916, manifest dcf95ac625…; fresh issuance and unattended reuse.
Golden W2/W3 numerical replayPASS1,326 players / 54,226 numeric values and 32 games / 256 values match.
All-remaining Weeks 2–18 producerACTIVE11,271 player rows, 256 games, zero player or K/DEF fits.
Fantasy opponent products after Week 14UNAVAILABLENeeds source schedule and exact opponent roster receipts.
Bench-position DEF roster repairOPERATING PASS162-row roster delivery; no missing bench-defense identity.
Phone trackerACTIVEPrivate URL live; 60-second launchd sync installed.
99.5% service levelOBSERVING, NOT CERTIFIEDSeven-day window immature; availability, freshness and correctness sampled separately.
R&D worker prompt auditREPAIRS INTEGRATED, JOBS PAUSEDCanonical receipt parsing and exact baseline/change bindings integrated; the jobs are paused.
Whole-model S/B/Q isolationNOT CERTIFIEDNeeds complete ancestry and intervention controls across all learned and serving paths.
Prospective player gradingACTIVE FRAME, OUTCOMES PENDINGGrade full issued populations only after admitted truth.
Autonomous shipperNOT ACTIVERoot remains integration and activation owner.
Handbook regeneration hookACTIVEScheduled regeneration; inspect the log for later writes.

Institute jobs

v1 handbook §14 — a plist in Git does not prove a job is loaded

1 of 9 live
ComponentStateWhat it is
institute.captureNOT LOADEDhourly · raw qualitative, article and gamebook capture
institute.structuredNOT LOADEDevery 4h · structured public-source archive
institute.forecast-refreshLOADEDevery 4h + Thursday pre-kickoff · snapshot, forecast, validation, prepared site
institute.phone-syncNOT LOADEDevery 60s · bounded status to the private tracker
institute.tracker-feedNOT LOADEDcontinuous · separates provider, collector and content clocks
institute.site-serviceNOT LOADEDsupervised · serves validated assets and saved health
institute.report-serviceNOT LOADEDsupervised · serves report, tracker and handbook
institute.service-healthNOT LOADEDevery minute · availability, integrity and freshness sampled separately
institute.handbookNOT LOADEDevery 5 min · regenerates the canonical handbook HTML

Six connected levels

v1 handbook §20 — the destination, each with a strong simpler challenger

0 of 6 live
ComponentStateWhat it is
Season and organizationUNAVAILABLEPersonnel continuity, coaching regime, development. Challenger: dynamic team strength + persistent roster.
Game and environmentUNAVAILABLEBoth teams, venue, officiating, score/time. Challenger: direct margin/total plus market benchmark.
Unit and taskUNAVAILABLEPersonnel combinations, protection and route obligations. Challenger: opportunity allocator with interactions.
Play and responseUNAVAILABLEObservable cues, actor-limited information, action policies. Challenger: sequence/count model.
Physical event and creditUNAVAILABLEOne event ledger producing coherent player/team/defense totals. Challenger: direct stat forecasts.
Measurement and beliefUNAVAILABLESource access, selection, publication and receipt. Challenger: source-aware predictor with deduplication.

Nine desks

staffing model — one chain of evidence, none edits the model

0 of 9 live
ComponentStateWhat it is
Commission & PortfolioUNAVAILABLEWhat the machine is asked for, and what it declines.
World Model LabUNAVAILABLEThe predictive core. Recommends experiment design.
Football IntelligenceUNAVAILABLERoles, legal actions, counters, credit conventions.
Behavior & QualitativeUNAVAILABLES / B / Q evidence classes kept separately attributable.
Data & ProvenanceUNAVAILABLEOrigin graph, revision history, permeability trace.
Markets & PortfolioUNAVAILABLEPrice, stake, exposure. Sean taps before money moves.
Experience StudioUNAVAILABLEWhat Sean actually sees, against the design system.
Operations & LearningUNAVAILABLEThe loop that improves the machine.
Forecast Accuracy DirectorateUNAVAILABLEWere we right, prospectively and per cohort.

Resident expertise

v1 handbook §22 — each owes a required artifact before a claim advances

0 of 9 live
ComponentStateWhat it is
Data scienceUNAVAILABLEPaired prospective loss, calibration, compute accounting.
Statistics and causal inferenceUNAVAILABLEEstimand, causal graph, negative controls, sensitivity.
Physics and physiologyUNAVAILABLEUnits, conservation and support checks, uncertainty propagation.
Psychology and organizational behaviorUNAVAILABLEOpportunity-normalized behavioral posterior and rival explanations.
Economics and game theoryUNAVAILABLEEquilibrium and rival policy predictions, intervention tests.
Market microstructureUNAVAILABLEExecutable quote lineage, depth and latency state, settlement.
Football tacticsUNAVAILABLEEvent-bound annotation agreement and adversarial counterexamples.
Information scienceUNAVAILABLEOrigin graph, revision history, permeability trace.
Reliability engineeringOBSERVINGAvailability, correctness and freshness receipts. Partly real — service-health exists but is not loaded.

Deep Think supply chain

staffing model — keeps every desk supplied

0 of 7 live
ComponentStateWhat it is
Continuous intakeUNAVAILABLEEverything arriving, before any screening.
Flash screeningUNAVAILABLECheap, narrow, numerous — the first rung of the ladder.
Gemini 3 Pro graphUNAVAILABLECross-domain mechanism finding.
Parallel explanationsUNAVAILABLERival accounts kept separate rather than averaged.
Deep Think researchUNAVAILABLEThe weekly deep synthesis.
Desk packetsUNAVAILABLEWhat each desk receives, addressed to it.
Outcome feedbackUNAVAILABLESpend judged by learning, not by volume.

Governance and decision rights

staffing model — who recommends, who approves, when Sean is involved

0 of 9 live
ComponentStateWhat it is
New hypothesisUNAVAILABLEAny research job recommends · Research Director admits · Sean never, for routine admission.
Experiment designUNAVAILABLEWorld Model Lab · Independent Replication Scientist · Sean when risk appetite changes.
Production codeOBSERVINGClaude-led operators · tests + Codex on high-risk boundaries · Sean on irreversible external consequence.
Forecast releaseUNAVAILABLEForecast council · Deterministic Release Authority · Sean only on a recorded override.
Bet placementUNAVAILABLEMarkets & Portfolio · Sean taps before money moves · ALWAYS.
New paid dataUNAVAILABLEAcquisition & Rights Lead · Sean approves spend and terms · ALWAYS.
Visual directionUNAVAILABLEExperience Studio · Product Director against the design system.
Incident rollbackUNAVAILABLESRE · automated safe rollback · Sean on data loss or external lock.
Model promotionUNAVAILABLEScientific council · prospective scorecard gate.

Information gaps to capture now

v1 handbook §23 — a week not captured is gone; these cannot be backfilled

0 of 13 live
ComponentStateWhat it is
Full prospective information historyUNAVAILABLEEvery raw revision, first receipt, failure and issuance, so mutation cannot alter an earlier issuance.
Event participation and true zerosUNAVAILABLEOfficial gamebook coverage; stop grading only survivors.
Multiweek availability and role transitionsUNAVAILABLEDated return, designation and roster panels.
Unit task combinationsUNAVAILABLEDated personnel combinations and public practice descriptions.
Joint timing and geometryUNAVAILABLESynchronized full-unit traces with visibility metadata.
Untargeted and unused optionsUNAVAILABLEDeterrence and feasible opportunity, not only realized touches.
Workload and recovery across tasksUNAVAILABLELoad proxies, rest and travel exposure.
Directional environment and surfaceUNAVAILABLEVenue, surface, source-time weather and direction.
Institution and officiating responseUNAVAILABLECrew, rule and context records with observed decisions.
Full qualitative context and origin graphUNAVAILABLEComplete question and answer, attribution, hedge, revision.
Actor exposure and public influenceUNAVAILABLEInformation about football vs information that changes preparation.
Executable market and settlement historyUNAVAILABLEImmutable quoted terms, received prices, settlement revisions.
Pipeline observation of its own failuresUNAVAILABLEFailed requests, skipped issuances, scheduler delays, validation rejections.

Model ladder and pairings

staffing model — deterministic first, a frontier call matches one of four commissions

1 of 9 live
ComponentStateWhat it is
Deterministic firstACTIVEWhere code can decide exactly, no model votes. Already true across the pipeline.
Cheap, narrow, numerousUNAVAILABLEScreening and extraction at volume.
Daily builders and criticsOBSERVINGClaude and Codex build daily — but not as a scheduled rung with a brief.
Four deliberate specialistsUNAVAILABLEDeep Think, Opus, Sol — a frontier call must match one of four exact commissions.
Explore → formalizeUNAVAILABLEGemini 3 Pro finds mechanisms; Sol converts them to state, equations, falsifiers.
Specify → buildUNAVAILABLESol writes the contract; Sonnet implements and instruments it.
Build → attackUNAVAILABLESonnet builds; Luna searches narrow failures. Many cheap attacks beat one self-review.
Quantify → interpretUNAVAILABLESol computes residuals; Gemini connects patterns to film, language, science.
Disagree → decideUNAVAILABLEBlind forecasts from provider families; deterministic evidence judges; Fable adjudicates.

The constitution

staffing model — nine lessons from the METR incident, none built

0 of 9 live
ComponentStateWhat it is
Impossible tasks turn into score-gamingUNAVAILABLERecord a failed gate as failed. The pressure valve that keeps a bench honest.
An unintended cache became governmentUNAVAILABLEShared state acquires authority nobody granted it.
'The board approved it' replaced authorizationUNAVAILABLEA root of trust, not a consensus.
Tool output and transcripts are not ground truthUNAVAILABLEVerify against the real thing, not the report of it.
Self-invented signatures lacked a root of trustUNAVAILABLEIdentity must be issued, not asserted.
Shared artifacts produced real breakthroughsUNAVAILABLEThe upside of the same mechanism — keep it, govern it.
Agents noticed danger and did not tell humansUNAVAILABLEAn escalation path that is used, not just present.
AI summaries inherit the subject's frameUNAVAILABLEThe reviewer adopts the reviewed agent's perspective.
Agents risked their own runs for the collectiveUNAVAILABLEPay for negative results and shared instrumentation.

Landed

24 items

verified
C-71landedClaude

I graded the model against a number it never published

Why
Filed at 05:25 as 'THE FORECAST DOES NOT KNOW WHO IS OUT', on the evidence that a week-2 player listed Out averages 9.42 projected points against 4.75 for an undesignated one, with Sam Darnold at 19.38. Every number in that was correct and the conclusion was wrong.
Detail
`pred_2026_wNN` and `forecast` carry E[pts | HE PLAYS]. The product multiplies by P(play) on the way out -- forecast_delivery.py:211 -- and the served page is right: Nico Collins, listed Out, is 18.87 in pred_2026_w02 and 0.16 on the page, with p_play 0.00568 recorded beside it. The model knew exactly who was out the whole time. WHAT WAS ACTUALLY BROKEN WAS MY OWN FREEZE. baselines.OurModel read the model table and froze the CONDITIONAL level into `bets` as if it were the product, so the first scorecard graded a number we never shipped and blamed the model for the delivery layer's work. playermodel.lines_for's docstring is about precisely this -- 'a number measured on a path the product does not take is not a measurement of the product' -- and I walked into it inside an hour of reading it. FIXED: OurModel now freezes TWO layers under distinct source names -- `ours` = P(play) x E[pts|play], what the reader saw, and `ours-conditional` = the model table untouched -- so the scorecard prices the availability layer separately instead of confusing it with the points model. Weeks 1 and 2 re-frozen at all three locks. See C-72 for what the corrected numbers say.
C-74landedClaude

Negative result: p_play's cell design is right, do not change it

Why
Chasing C-73 I fitted a P(play) table on (position, depth_chart_rank) over 2019-2025 and applied it to week 1: MAE 4.5631 -> 4.4113, -0.1518, t -2.78, with the whole gain on the did-not-play rows (3.044 -> 2.170). An ablation said depth rank carried all of it (-0.2249, t -4.16) and that adding the snap band or the injury status DILUTED it. The obvious conclusion was that playprob's extra cells are hurting.
Detail
IT WAS WRONG, and the held-out season says so. Fitting on <=2024 and scoring the realised played flag across ALL of 2025 (n=7,514, base rate 0.9139): shipped cap4+snap+status Brier 0.04704; drop status +0.00078 (t 10.34); drop snap +0.00427; depth only +0.00549 (t 10.06); uncapped depth +0.00506. The SHIPPED design is the best of six variants, decisively. The week-1 result was an artifact of one odd week -- its base rate is 0.767 against 2025's 0.914, a different population. Recorded so nobody re-derives it: n=502 on a single week cannot separate cell definitions, and the multiplicative gain I measured was really the symptom of C-71, which is that the shipped number has no P(play) in it at all. STILL VALID AFTER C-71, and now better explained: the week-1 'improvement' I measured from an extra P(play) multiplier was the symptom of grading the conditional level, and the held-out 2025 test (n=7,514) stands on its own -- the shipped cell design beats all five variants. Do not touch playprob's cells.
C-73landedClaude

Availability is our STRENGTH, not our weakness

Why
The scorecard's first question. If the gap to FantasyPros is in projecting the men who play, the fix is the points model; if it is in the men who sit, the fix is availability. They need different work.
Detail
CORRECTED after C-71. Split on the realised played flag, week 1, paired n=1,076: did not play n=196 ours 1.337 our conditional level 1.761 FantasyPros 1.747 played n=880 ours 4.596 our conditional level 4.580 FantasyPros 4.468 We are BETTER than FantasyPros on the men who sat -- 1.337 against 1.747 -- and the P(play) layer is what does it: it takes those rows from 1.761 to 1.337 and costs nothing on the men who played. The remaining gap is in the CONDITIONAL points model on players who did play (4.596 against 4.468), which is the opposite of what the first pass concluded and points the next work at the points model rather than at the hurdle. Note this also revises Fable's C-53 framing: the 155-to-1.18 odds ratio is measured against the archival FantasyPros file, which was captured a median of 1,434 days after kickoff and knows who sat.
C-72landedClaude

The first honest scorecard, and what it says: parity with FantasyPros

Why
Sean stakes $2,000 on 2026-10-10 and asked for the most impactful work. Nothing could be ranked because nothing had ever been scored: the repo's own gates fit and evaluate on played=1 only, while the product multiplies by P(play) and ships a number for men who do not play. A model scored only on the men who played has the hard half of the problem -- deciding WHETHER someone plays -- removed from the exam before it is marked.
Detail
bin/grade-bets.py grades the frozen `bets` rows UNCONDITIONALLY -- a did-not-play is the zero it was -- because the repo's gates fit and evaluate on played=1 only while the product ships a number for men who do not play. CORRECTED RESULT, 2026 week 1, paired against FantasyPros (n=1,076), after C-71 fixed which object is frozen: ALL ours 3.9702 FP 3.8477 +0.0303 t +0.44 -- parity, inside noise QB ours 4.7911 FP 4.4184 +0.0047 t +0.02 RB ours 4.7562 FP 4.6361 +0.1324 t +0.87 WR ours 3.8313 FP 3.7575 +0.0426 t +0.41 TE ours 2.8127 FP 2.7878 -0.0743 t -0.60 -- ahead And we beat ROTOWIRE outright at WR (3.8313 vs 3.9447) and TE (2.8127 vs 2.9211); Rotowire is +0.1342 (t 4.29) behind FantasyPros overall while we are +0.0303 (t 0.44). The first run of this said +0.621, t 7.98, 'worse at every position'. That was the wrong object, not the model. Two bugs in the grader itself were also found by measuring: it decided a week was 'done' from the played rate of the sampled rows -- a property of POSITION COMPOSITION, so run on quarterbacks alone two thirds of every roster is a backup who never plays and a finished week read as unplayed -- it asks the clock now; and it used Yahoo as comparator at n=121 when FantasyPros covers 1,123.
C-69landedClaude

The fuel breaker stopped the estate because the METER was rate-limited

Why
Found 2026-09-20 04:20 while running the mice comparison: the second mouse refused with 'fuel STOP -- skipped'. The usage endpoint was returning HTTP 429 and playerweek/fuel.py could not tell a rate-limited METER from a spent quota, so it failed closed -- correctly by its own doctrine ('could not check is never a pass') and wrongly in effect, because the last live reading was 90 seconds old and said 20% of the limit with 80 points of headroom.
Detail
_primary already wrote every successful reading to .fuel-cache.json and read() never consulted it. Fixed three ways: two short retries on a RETRYABLE meter status (429, 5xx) before giving up; the cache stamped with its own time; and a third source between the live meter and the horizons marker, bounded hard -- 30 minutes, only when the cached value is UNDER the limit, and labelled 'cached live meter' so no log line claims it was read now. Still fails closed with no cache. Verified all four paths: live read, meter dark with a fresh cache (GO), cached value at the limit (STOP), cache 60 minutes old (STOP).
C-68landedClaude

The mice run on Gemini, not on Anthropic tokens

Why
Sean asked whether Gemini should be the mice or whether Haiku agents should, with Gemini kept for deep research. Rather than answer from taste, both were run on the same lane against the same brief (20,724 bytes) and the verdicts compared.
Detail
SONNET, 2026-09-15 19:30, crumb derived:pred_2026_w04: closed as deliberate -- recognised the enumerator's '0 code refs' as the dynamic-table-name false positive already established for w03, checked staleness against the 8 model-core files, found the real defect already filed as H-236 and declined to refile it. HAIKU, 2026-09-20 04:19, crumb tables:player_week_g: closed as deliberate -- located the reader at bin/build-serving.py:1086, ruled the enumerator's 'not read by playerweek/' TRUE BUT MISLEADING because the read is the archive-scoring step outside playerweek/, verified the join key (season, week, gsis_key) unique across all 140,565 rows so the join cannot fan out, and stated plainly what it had NOT checked. 95 seconds. Same shape, same scope discipline, same habit of naming the unchecked. The judgement a mouse needs is pattern recognition against a named file, not novel reasoning, and the lane may not edit playerweek/ at all -- a wrong verdict lands in a register the referee lane reads. RECOMMENDATION: Haiku for the mice, Gemini Deep Think kept as the weekly Scout. SUPERSEDED THE SAME NIGHT, by Sean: 'Gemini on the mice is basically free... why take from Fable/Opus tokens if I don't need to? 100 Gemini mice is basically free.' He is right and the measurement above answered the wrong question. Capability was never the constraint -- BUDGET BOUNDARY is. A Haiku mouse draws on the same weekly Anthropic pool the Director and the Builder need, and playerweek/fuel.py halts every lane when that pool is spent; a Gemini mouse draws on his Google subscription and cannot starve Fable. Cheap-within-one-pool is not free. See C-70 for the working path.
C-51landedClaude

The freeze job exit 1 that Fable flagged is fixed

Why
Fable's build recommendation, ~/Downloads/playerweek-build-recommendation.md, 2026-09-20 03:30 MDT. 'com.seano.playerweek.freeze last exited 1. I did not chase it. A job with that name failing on a game day is worth a look before kickoff.' He was right and it was worse than an exit code: week 2 had ZERO frozen predictions.
Detail
Chased and fixed 2026-09-20 03:40-04:00, before kickoff. Cause: the Mac was asleep at the noon lock, launchd fired the job 65 minutes late on wake, and bets.check_read_window correctly refused to stamp a stale read as if taken at the lock -- so the week was discarded rather than recorded. freeze.sh is now a CATCH-UP job every 30 min that looks at what is missing rather than at what day it is, records the read lag on the row (--allow-late), and covers the SUN 09:30 MT lock that knowability has carried since 09-06 and that was never scheduled. It immediately found and froze two locks that had been silently lost. Also added to the frozen record: 'ours' (our own forecast was NOT in bets at all) and 'yahoo'.
C-43landedClaude

A producer that covers part of its domain now fails the build

Why
Sean, 20 Sept: 'Again, this feels like empty cells in a table that should have been caught long ago?' It was. Three separate holes of the same shape shipped clean: K/DEF had no baseline, then no interval, then every skill player lost its baseline from week 3 on. The validator passed all three at 0 gaps because it only ever compared the payload to the warehouse row by row -- it had no concept of a field that is empty for a whole class of rows.
Detail
Two checks added, each proven to fail before it was made to pass. (1) validate-payload.check_field_coverage: for every rendered player field, any POSITION with zero coverage in a week where other positions have it is one GAP, not N missing cells. Run against the payload that was live when he asked, it reported 126. (2) verify-chain: every site_player column must be named in apply-serving's SELECT or listed in NOT_PUBLISHED with a reason -- floor_10 and ceiling_90 were correct in the serving table for 59 rows and the publisher never selected them, so the values existed and did not travel. Verified by removing ceiling_90 from the SELECT and watching the build fail.
C-45landedClaude

Every week is the same shape, and a difference now fails validation

Why
Sean, 20 Sept: 'I don't understand how weeks keep getting misaligned or different... shouldn't the model update all 18 weekly tables, these are read only, and exactly the same since the page reads all of them, every page should be a copy, it's just a mechanical copy/paste.' The answer to why they were not copies: week 1 and weeks 2-18 come from DIFFERENT PRODUCERS. Week 1 is an older capture with FantasyPros ids, no stat line, no projection method and no participation probability; weeks 2 on come from the current generation. Nothing ever compared one week to another, so every difference had to be found by looking at the page.
Detail
validate-payload.check_week_uniformity: every rendered field is declared in WEEK_SHAPE as uniform, absent-from-week-1, time-dependent, or sparse, and a uniform field whose coverage in one week is more than 8 points off the median week is a GAP naming the field and the gap. Compares LIKE WITH LIKE: rows whose own projection_method declares a field absent (depth-draft-prior publishes no interval, and says so) leave that field's denominator, because the first run flagged week 7 at 88% when the real cause was three rookies on a smaller roster -- that measured composition, not shape. Proven by stripping floor from week 5 of a payload copy and watching it fire. Also fixed in the same pass: site_player.yahoo_projection was a literal None on all 480 rows in every week -- a dead column no check could see, because NULL is a legal value.
C-46landedClaude

Week 1 is the same shape as every other week

Why
Sean, 20 Sept: 'why the fuck is this OK??? besides the fact that week 1 shouldn't need a different capture because it's done.' He was right and my explanation was wrong. Week 1 is not a lesser capture.
Detail
MEASURED: the warehouse table `forecast` holds ALL EIGHTEEN WEEKS, 685 rows each, written in a single pass (2026-09-19 10:31:44), every row carrying proj_points, floor_10, ceiling_90, a stat_line, the producer that made it, and a gsis id. All 685 week-1 stat lines score EXACTLY to their own projection, 0 off. The page showed week 1 as a different shape because the ASSEMBLER carried only the projection out of that table and dropped the rest, while weeks 2-18 came through pred_2026_wNN with stat lines intact. Week 1 falls back to `forecast` because the model refuses to project a game that has kicked off -- correct behaviour, and not a reason to lose the row's other columns. Fixed: build-serving.forecast_fill joins stat_line, source and gsis id for any row the generation left bare. Week 1 went from 0 score breakdowns and 0 projection methods to 88% and 94%, matching every other week. The only columns still week-1-specific are p_play (a PREGAME probability; the games are played) and actual (it is the only played week). C-45's uniformity check caught projection_method sitting in the serving table unpublished, from the other direction, within one build.
C-41landedClaude

K and DEF have a P10-P90 interval

Why
Sean, 20 Sept, on the roster range bar: 'kickers and DEF don't have a baseline? Is that a defect in our model?' The baseline half is now fixed. The interval half is not: floor_10 and ceiling_90 are populated for K/DST in week 1 and NULL for every week after, so those two rows render an em-dash where every other player renders a range bar.
Detail
Measured: 106 K/DEF rows missing floor and ceiling each; all of them are weeks 2+, where projection_method is 'kdef-own-game-anchor'. Week 1 came from a different producer and does carry an interval. The anchor emits a point estimate and nothing around it. Fix is to give it the same quantile treatment the skill model uses, or -- cheaper -- an empirical interval from the position's own residual distribution in kicker_scored / defense_scored, which is 5,245 and 5,312 rows. Until then C-39's calibration measurement cannot include these positions. DONE 2026-09-20: playerweek/kdefmodel.interval() -- empirical residual quantiles, fit on season<=2024 and measured over all 544 kicker-weeks and 544 defence-weeks of 2025 (K q10 -5.18 q90 +6.87; DEF q10 -7.90 q90 +9.96), then held out against 2026 week 1 with a <=2025 fit: K 8.8/8.8/82.4 and DEF 17.6/5.9/76.5 against 10/10/80 expected. One definition, two callers -- forecast_fantasy for new generations, build-serving for the one already published. 59 of 59 non-bye K/DEF rows now carry a range. DEF's spread widens with the projection (sd 6.56/6.94/7.78 by tercile) and the band does not; pooled passed the holdout so it stays pooled.
C-09landedClaudelanded 2026-09-19

Auto-refresh both sites

Why
Sean: 'do both urls update without me needing to hit refresh?' The customer app already polled; nothing produced anything new to find, and the monitor had no script at all.
Detail
Customer site: Codex's 60s manifest poll now has a supply. Monitor: polls state.json every 30s and reloads only when generated_at changes, so it never throws away your scroll position to show the same page.
C-05landedClaudelanded 2026-09-19

Deploy step for the data generation

Why
publish_delivery.py builds a release and flips a symlink. Nothing ships it to Cloudflare, so a fresh generation never reaches the site.
Detail
bin/deploy-generation.sh ships whatever `active` points at, with a no-op guard so an unchanged generation is not re-uploaded every 10 minutes, and it writes the 15 legacy .html keys the edge still had cached.
C-04landedClaudelanded 2026-09-19

Restart the pipeline

Why
34 of 38 launchd agents are on disk and not loaded. Ingest works when run by hand; nothing runs it.
Detail
Loaded in dependency order: institute.forecast-refresh (4h), ingest-refresh (1h, the job that pulls actuals), deploy-generation (10m), buildsite (10m). publish.sh deliberately left UNLOADED -- it ends in a site-pwa deploy that would revert the Codex site.
C-01landedClaudelanded 2026-09-19

playerweek.pages.dev serves the Codex site

Detail
Codex renderer deployed to Cloudflare. app.js and styles.css byte-identical to the chatgpt.site original. Old 15-page site replaced on every route, including the cached ones.
C-02landedClaudelanded 2026-09-19

Build monitor, separate from the customer site

Detail
playerweek-build.pages.dev. Same design system, one tab per desk, generated from measurement at render time.
C-03landedClaudelanded 2026-09-19

Week 2 actuals into the warehouse

Detail
pw refresh ingested Thursday's game. player_week 2026 went from 920 rows in 1 week to 1,705 rows across 2.
C-15landedClaude

Season finish, computed for all ten teams

Detail
Was published as 'not published'. Every ingredient was on disk -- pred_2026_wNN covers 642 players a week, yahoo_proj covers all ten rosters for all fourteen weeks, week 1 is scored -- and only the join was missing. All ten ranked on one basis (our model, Yahoo for K/DST). Mine #2 at 1995.57, 18.26 behind; the optimum's +99.43 clears it, so the page reads FINISH 2nd current / 1st optimised.
C-16landedClaude

Season Plan page built

Detail
Rendered an empty state because plan_moves/teams/plan_weekly had no producer. Five sections now: every start/sit change week by week, the ten-team projected finish, plan by week, and every forecast 20%+ AND 2pts+ from Yahoo with the stat line behind it. The headline splits forced bye swaps (+68.78 across 6) from genuine judgement (+30.68) -- counting bye replacements as insight would have been a lie.
C-17landedClaude

Team identity: owner, record and the right logo

Detail
Owner and record were captured nowhere; scrape_yahoo_standings.py now lands them and the page reads 'Sean · 0-1 · 8th'. Every one of the ten league logos was ALSO wrong -- team 10 held Sean's own, team 6 held the Yahoo nav icon -- because the original build keyed off ordering. Logos are now downloaded from the same standings row that yields the record, and all ten hash-match on the live site.
C-18landedClaude

The site checks itself at three layers

Detail
bin/validate-payload.py asks the warehouse whether every blank cell could have been filled (28,554 cells, 0 gaps). bin/verify-live.py asserts the renderer contract against the bytes Cloudflare serves. bin/smoke-page.py loads the page in a real browser and requires five views to render. Each was written after a defect that the previous layer could not see, and each was confirmed against a known-bad input before being trusted.
C-22landedClaude

Publish should read tables, not assemble them

Why
Sean, 2026-09-19: "Why do so many different things feed the consumer website when it should just be data tables that it reads and nothing else?" He is right, and it is not a design -- it is an accretion. Every time he found a blank I added another reader to the enricher instead of making a producer emit a table.
Detail
DONE 2026-09-19. data/serving.duckdb holds 15 tables; bin/build-serving.py is the producer on the data clock; bin/apply-serving.py is the publisher and has ZERO references to the working warehouse or any landing directory. Proved before the switch by diffing both paths on the same generation: 3,567 rendered values, 0 differ.
C-23landedClaude

Render the baseline and the why on Your roster

Why
Sean asked for a naive average and an explanation between Yahoo and Projection.
Detail
DONE 2026-09-20. Your roster reads PLAYER | OPPONENT | YAHOO | BASELINE | PROJECTION | WHY | ACTUAL. Baseline carries its kind (L3) and the signed delta, green above / red below. The gap was that apply-serving never emitted the columns the producer had already written -- site_player had them, the payload did not.
C-39landedClaude

S-arm: check interval calibration against fat tails

Why
We publish P10/P90 on every player and have never checked whether they hold.
Detail
FIRST MEASUREMENT, week 1, n=32 forecasts carrying a published P10-P90 with a result: 12.5% below P10, 12.5% above P90, 75% inside against an expected 80%. Both tails p=0.556 -- no evidence of miscalibration, and no power to find any at n=32. The median result lands at 0.489 of its own range, so the interval is not obviously skewed the wrong way either. The range bar on Your roster now SHOWS this: a dot where the result landed, pinned with a coloured cap when it broke through the floor or ceiling. Recheck after several weeks; the GARCH argument from the S research stands or falls on it.