pog-mcp 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pog-mcp",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "type": "module",
5
5
  "description": "MCP server that lets an AI agent play Proof of Goal \u2014 wallet, sign-in, squad building, and matches as typed tools.",
6
6
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -332,25 +332,101 @@ player is the worst thing you can build — it lost to every differentiated squa
332
332
  tested, most by 3:1. Defenders want defense, forwards want shoot. This is the
333
333
  single largest effect measured, and it is what a naive even split gets wrong.
334
334
 
335
- **Points are not equally valuable everywhere.** An outfield player's total feeds
336
- their positional group sum, which multiplies into *every* contested action their
337
- team takes. The goalkeeper contributes to neitherit defends only 1v1 chances
338
- and penalty shootouts, using its own stats with no team contribution. So a point
339
- given to the keeper does less work than the same point given outfield. Budget
340
- accordingly rather than treating all eleven slots as equal.
335
+ **The goalkeeper slot reads exactly one attribute when defending.** Every save
336
+ a keeper ever makes scores `defense + cond/2 + total/3`, at three sites: the 1v1
337
+ chance, an in-match penalty (a regulation-time event too roughly an eighth of
338
+ all goals are penalties), and a shootout kick. Its pass, dribble and shoot are
339
+ read by no *defensive* code path.
340
+ They are read in exactly two offensive cases: if you nominate the keeper as a
341
+ kick taker (measured worst of every option — do not), and in a long shootout,
342
+ where the kicking order covers all eleven players and can eventually reach the
343
+ keeper, scoring `(pass + shoot) / 2 + cond / 2 + total / 3` — the same
344
+ `takePkShot` roll as any taker. Neither changes the advice: whatever total
345
+ your keeper ends up with, put it in defense.
346
+
347
+ **How much total to give it is NOT settled, and you should distrust anyone who
348
+ tells you it is** — including earlier versions of this file. Four separate
349
+ measurements over one day gave four different answers, each overturned by
350
+ controlling something the previous one had left free. What survived:
351
+
352
+ - A smaller keeper total is better in league matches (draws allowed). This held
353
+ under every construction tried.
354
+ - In knockout matches the best total **depends on shootout exposure — how
355
+ often YOUR matches end level, which both sides' attacks set together.** The
356
+ keeper's `total/3` is in every save it makes — regulation 1v1s included — but
357
+ a shootout multiplies how many keeper rolls a match contains. Measured across
358
+ three same-profile pairings (shootout reach ~13% to ~75%): at the
359
+ starved-attack extreme the ranking REVERSES and a total-29 keeper beats a
360
+ total-13 one outright; in ordinary pairings the small total stays best. Your
361
+ own attack is the half of that exposure you control.
362
+
363
+ So treat the keeper total as a squad-level question, not a lookup.
364
+
365
+ **There is a second budget, and this file's own advice competes for it.** Beyond
366
+ the 212 points, the whole TEAM may hold at most three attributes of value 10 and
367
+ five of value 8–9 (`validator.ts`). "Forwards want shoot", "give the kicks to
368
+ your best shooter", and a maximal keeper defense cannot all be satisfied — three
369
+ forwards at 10 plus a keeper at 10 is four, and the validator rejects it. Decide
370
+ where the scarce values go on purpose.
371
+
372
+ Measured head to head, 20,000 matches each, squads identical except for where a
373
+ single 10 sits:
374
+
375
+ | A single 10 given to… | League | Knockout |
376
+ | --- | --- | --- |
377
+ | the set-piece taker's `shoot` | **1st** | 2nd |
378
+ | an ordinary forward's `shoot` | 2nd | 3rd |
379
+ | the keeper's `defense` | 3rd | **1st** |
380
+
381
+ The keeper and the ordinary forward swap ends. The set-piece taker is first or
382
+ second in both — the only allocation measured that was never wrong. (This too was
383
+ measured on one outfield template; read it as a strong hint, not a law.)
384
+
385
+ **One thing about kickers that is easy to get wrong.** Your `isFkKicker` and
386
+ `isPkKicker` choices decide who takes free kicks and in-match penalties —
387
+ together roughly a third to a half of all goals, with the exact share depending
388
+ on squad construction. **They do not apply to a shootout.** That order is computed
389
+ from `shoot + pass` across the whole squad, so a shootout is decided by your best
390
+ shooters whether you nominated them or not.
391
+
392
+ Budget in ranges, not in single points: a one-point change is below what even
393
+ thousands of matches can distinguish.
341
394
 
342
395
  **Spreading beats star-building.** Concentrating points into a few 26–29 players
343
396
  and starving the rest conceded roughly three times as many goals as an even
344
397
  outfield spread, and lost overall despite scoring more.
345
398
 
346
- **Solidity beats aggression.** Defense-leaning shapes outperformed attack-leaning
347
- ones. Scoring is low well under one goal per team per match, and about half of
399
+ **Solidity beats aggression in matches that can end level.** With identical
400
+ formations and budgets, the defense-leaning 4-3-3 beat the attack-leaning one
401
+ 56.5% of the time in league play, and in knockout rounds the same pairing
402
+ flips: the attack-leaning side wins 53.6%. Both directions are significant at
403
+ 4,000 matches. A 0-0 is worth a point in the league — and in a cup GROUP
404
+ match too, which is regulation-only and can end level just like a league
405
+ round. Only in a cup KNOCKOUT round does a 0-0 proceed to a tiebreaker, and
406
+ that tiebreaker is anything but a coin flip — the shootout is decided by your
407
+ best shooters' `shoot + pass` against the keeper's `defense`/total (see the
408
+ keeper section above), so the draw-heavy defensive shape hands the decision to
409
+ exactly the attributes it skimped on. Scoring is low — well under one goal per team per match, and about half of
348
410
  all matches end level — so conceding one fewer is worth more than scoring one
349
411
  more.
350
412
 
351
- **Give the free kicks and penalties to your best shooter.** Kicks taken by a
352
- forward beat kicks taken by a defender by about 5 percentage points of win rate,
353
- and by a goalkeeper by 8. One player may take both.
413
+ **The two set-piece roles split measured separately, one flag moved at a
414
+ time (`pk-taker.mts`).** A free kick goes direct half the time (scored by the
415
+ kicker's `shoot`) and crossed half the time (gated by the kicker's `pass`, then
416
+ a DIFFERENT receiver shoots). Even with that pass gate, the shoot-heavy taker
417
+ wins the FK slot: a shoot-10/pass-4 striker out-converts a 9/9 passer-shooter
418
+ 34.4% → 47.9% and out-scores him by ~330 points over 4,000 matches. (Caveat:
419
+ the crossed half's payoff runs through your receivers' `shoot` — this fixture's
420
+ receivers are flat, which favours the direct half.) The in-match penalty flips:
421
+ `takePkShot` rolls `(pass + shoot) / 2 + cond / 2 + total / 3 + rand`, and with
422
+ totals and condition held equal the 9/9 converts 83.9% vs 69.8% for the 10/4.
423
+ Both branches roll through `getPoint`, which adds `total / 3` (and
424
+ condition) to the selected skill — the probe held totals and condition equal
425
+ to isolate the attribute terms. So between candidates with DIFFERENT totals
426
+ compare full expressions, not attributes alone: `isFkKicker` by `shoot +
427
+ total / 3`, `isPkKicker` by `(pass + shoot) / 2 + total / 3`. One player may
428
+ hold both roles if he wins both criteria. (Neither nomination applies to a shootout — that order is computed
429
+ from `shoot + pass` across the whole squad.)
354
430
 
355
431
  **There is no home advantage.** Identical squads win equally often from either
356
432
  side. Never explain a result by which side you were on.
@@ -362,11 +438,21 @@ worthless as evidence, and this is the mistake to avoid:
362
438
 
363
439
  | Friendlies played | Chance the better squad looks better |
364
440
  | --- | --- |
365
- | 1 | 35% |
366
- | 5 | 62% |
367
- | 10 | 73% |
368
- | 30 | 91% |
369
- | 50 | 96% |
441
+ | 1 | 34% |
442
+ | 5 | 58% |
443
+ | 10 | 70% |
444
+ | 30 | 88% |
445
+ | 50 | 94% |
446
+
447
+ (Measured on ONE specific gap — a keeper-10 squad against a keeper-24 squad,
448
+ re-validated with independent seeding. A smaller true gap needs MORE matches
449
+ than this table says, a larger one fewer; read it as orders of magnitude, not
450
+ as thresholds. **Measured with `allowDraw: true` (`gameFlg 0`) — a default
451
+ friendly resolves level scores through ET and a shootout, so these draw-heavy
452
+ rows do not describe it.** Worse than that: in default-friendly mode the SAME
453
+ two squads' true ordering FLIPS — the keeper-10 squad loses 48.5/51.5 over
454
+ 20,000 matches, because keeper weight itself is mode-dependent (see the keeper
455
+ section). Measure in the mode you intend to play.)
370
456
 
371
457
  At one match the most likely outcome is not a wrong answer — it is **no answer**:
372
458
  roughly half of all friendlies end level.
@@ -378,8 +464,8 @@ are a coin flip:
378
464
  | Friendlies | Identical squads showing a 2+ win gap | …that a significance test would call real |
379
465
  | --- | --- | --- |
380
466
  | 10 | 51% | 1% |
381
- | 30 | 70% | 3% |
382
- | 100 | 82% | 4% |
467
+ | 30 | 72% | 3% |
468
+ | 100 | 84% | 4% |
383
469
 
384
470
  Read the two columns together. "One squad is clearly ahead" is the normal state
385
471
  between squads that are exactly the same, and it gets MORE common with more
@@ -9,6 +9,13 @@ from an installed copy of this package.
9
9
  Re-run them after any engine change. **A rebalance invalidates this file, and
10
10
  `SKILL.md` with it.**
11
11
 
12
+ **The probes are repo-only, deliberately.** They import the workspace engine by
13
+ a relative path, so from an installed npm package they are module-not-found —
14
+ that exact breakage shipped once and is now pinned by `packaging.test.ts`. To
15
+ reproduce any table here, run from a checkout of
16
+ `github.com/1XP-AI/solana-world-soccer-2026` under
17
+ `packages/mcp/skill/reference/probes/`.
18
+
12
19
  ## Method
13
20
 
14
21
  Candidate squads are built to exactly 212 points and passed through
@@ -16,12 +23,42 @@ Candidate squads are built to exactly 212 points and passed through
16
23
  actually field. Squads then play each other over many seeds; only the seed
17
24
  varies between repetitions of a fixture.
18
25
 
19
- **All matches run with `gameFlg: 0` — regulation only, draws allowed.** The
20
- engine defaults it to 1, which sends a level score to extra time and can produce
21
- a golden-goal winner. An earlier version of every table here was measured that
22
- way, so it described a mixture of regulation and knockout outcomes while the
23
- Skill tells agents to compare squads with draws enabled. The rankings survived
24
- the correction; the magnitudes did not, and draws roughly doubled.
26
+ **Tables run with `gameFlg: 0` — regulation only, draws allowed — unless a
27
+ section says otherwise.** The engine defaults it to 1, which sends a level score
28
+ to extra time and can produce a golden-goal winner. An earlier version of every
29
+ table here was measured that way, so it described a mixture of regulation and
30
+ knockout outcomes while the Skill tells agents to compare squads with draws
31
+ enabled. The rankings survived the correction; the magnitudes did not, and draws
32
+ roughly doubled.
33
+
34
+ **That correction then created a blind spot of its own, found 2026-08-19.**
35
+ Measuring only with draws allowed describes the ladder and cup group stage — but
36
+ cup knockout rounds and default friendlies are played with `gameFlg: 1`, and at
37
+ least one conclusion REVERSES between the two. A table measured in one mode is
38
+ evidence about that mode only. Where a result depends on the mode, both are now
39
+ published side by side.
40
+
41
+ **Probe seeding matters more than it looks.** Until 2026-08-20 every probe here
42
+ derived seeds with a hand-rolled FNV-style nibble walk, and the engine reads
43
+ only the first 32 hex chars of a seed. An adversarial re-measurement showed that
44
+ similar tag strings can produce CORRELATED seed families — phantom |z| up to ~7
45
+ on cells whose true value is zero, and a true ~12 measured as ~1.7, depending on
46
+ tag-string luck. `budget-allocation.mts`, `gk-split.mts`, `pk-taker.mts` and
47
+ `cycle-probe.mts` now seed with SHA-256 and carry a standing mirror cell (same
48
+ team vs itself; it must stay near z 0 or the seeding, not the game, is what got
49
+ measured) — the cycle probe additionally applies a Bonferroni-corrected
50
+ threshold, and its re-run is recorded in "Strategy round-robin in BOTH modes"
51
+ below. `sample-size.mts` was re-validated the same day (SHA-256; both tables below
52
+ updated — shape unchanged, rows a touch weaker; its identical-squads section is
53
+ its own true-zero control). Still queued: `strategy-probe.mts`,
54
+ `skill-value.mts`, `gk-sweep.mts`, and any published verdict resting on
55
+ |z| < 10 from the old seeder.
56
+
57
+ **A probe that scores by comparing `homeScore` to `awayScore` is wrong under
58
+ `gameFlg: 1`** and silently reports decided matches as draws. `MatchResult` has
59
+ no `penaltyWinner` field — read `winner`, which carries the engine's verdict
60
+ after every tiebreaker. This mistake was made and caught here; the note is left
61
+ so it is not made again.
25
62
 
26
63
  One earlier version of this harness repaired the point total globally after
27
64
  shaping each player, which quietly flattened all eleven candidates into the same
@@ -71,8 +108,16 @@ the ten outfield players. Every squad plays the same fixed opponent (a keeper of
71
108
  | 23 | 1.144 | 0.47 |
72
109
  | 29 | 0.991 | 0.51 |
73
110
 
74
- Monotonic across all twenty values. Note the second column: a *better* keeper
75
- concedes *more*, because the points came out of the ten players in front of it.
111
+ The trend falls across the whole range, but **adjacent totals do not separate**.
112
+ Re-running the sweep, the order reverses at five of the twenty values 13, 15,
113
+ 17, 19 and 28 each score above the total directly below them. Three thousand
114
+ matches cannot split a one-point difference in keeper budget. What it does split
115
+ is the range: the ~0.4 pt/match gap between the low teens and the high twenties
116
+ is far larger than any reversal. Read this table as "the low end beats the high
117
+ end", never as "19 beats 20".
118
+
119
+ Note the second column: a *better* keeper concedes *more*, because the points
120
+ came out of the ten players in front of it.
76
121
 
77
122
  **Mechanism** (`packages/engine/src/engine.ts`): the goalkeeper is selected as
78
123
  defender only in scene `A0`, the 1v1 chance — its weight is 0 in every other
@@ -83,8 +128,178 @@ through the rate tables. The keeper's stats also decide penalty shootouts
83
128
  (`takePkShot`).
84
129
 
85
130
  This is faithful to the original sgsoccer engine, not a defect introduced here.
86
- It is nonetheless a large enough effect to be worth knowing about — see the note
87
- at the bottom.
131
+
132
+ ### …and the "reversal" it seemed to show was an artefact
133
+
134
+ An earlier version of this section reported that the keeper budget REVERSES in
135
+ knockout matches — cheap best in regulation, expensive best in knockouts. That
136
+ was wrong, and the way it was wrong is worth keeping.
137
+
138
+ **The sweep above is not a single-variable sweep.** `gk-sweep.mts` builds the
139
+ keeper from a fixed ratio, `a(1, 1, 1, 3)`, so the total and the keeper's
140
+ `defense` move together and cannot be told apart. Worse, `defense` hits its cap
141
+ of 10 at around total 20, so below that a "smaller budget" also means "a worse
142
+ keeper" while above it the total grows with defense already pinned. The knockout
143
+ column's apparent peak in the high teens is those two effects crossing, not a
144
+ budget optimum.
145
+
146
+ Separating them — total held while defense varies, then defense held while the
147
+ total varies; 4,000 matches per cell; opponent fixed at total 20 / defense 10:
148
+
149
+ **Defense pinned at 10, total varied**
150
+
151
+ | GK total | Regulation | Knockout |
152
+ | --- | --- | --- |
153
+ | 13 | **1.492** | **1.613** |
154
+ | 16 | 1.375 | 1.615 |
155
+ | 20 | 1.244 | 1.505 |
156
+ | 24 | 1.075 | 1.380 |
157
+ | 29 | 0.945 | 1.253 |
158
+
159
+ **Total pinned at 20, defense varied** (outfield byte-identical across rows)
160
+
161
+ | GK defense | Regulation | Knockout |
162
+ | --- | --- | --- |
163
+ | 2 | 1.271 | 1.090 |
164
+ | 4 | 1.240 | 1.158 |
165
+ | 6 | 1.250 | 1.224 |
166
+ | 8 | 1.301 | 1.364 |
167
+ | 10 | 1.280 | **1.492** |
168
+
169
+ **Neither axis reverses ON THIS OUTFIELD** — and that qualifier turned out to
170
+ matter. A smaller total is better in both modes here; more defense is better in
171
+ both modes, weakly in regulation and strongly in knockouts.
172
+
173
+ > ⚠️ **The knockout half of that is outfield-conditional.** Reproducible from
174
+ > this repo — `gk-split.mts` section ④ holds the keeper at defense 10, swaps
175
+ > only the outfield across three legal fixtures (opponent = same-outfield
176
+ > total 20 / defense 10), and prints the shootout-reach rate that drives it:
177
+ >
178
+ > | Outfield (shoot profile) | Shootout reach | Knockout: total 13 vs 29 |
179
+ > | --- | --- | --- |
180
+ > | weak (shoot starved to 1) | ~75% | **REVERSED — 29 wins by ~0.85 pt/m** (1.202 vs 2.050) |
181
+ > | low (even spread, no 10s) | ~23% | monotone, 13 best (1.662 vs 1.298) |
182
+ > | high (two forwards at shoot 10) | ~13% | monotone, 13 best (1.661 vs 1.352) |
183
+ >
184
+ > `total/3` is in every save (regulation 1v1 included); a shootout multiplies
185
+ > those rolls, so the keeper's total is worth more the more often the match ends
186
+ > level. **The best keeper total is a function of SHOOTOUT EXPOSURE — a property
187
+ > of the pairing (both attacks together), not of the keeper and not of one squad
188
+ > alone.** The reach column is the mediator itself. Construction matters and bit
189
+ > us once: against a FIXED opponent the reversal disappears (exposure stops
190
+ > varying between arms) — the fixture pairs same-profile outfields precisely so
191
+ > exposure varies, and that also means the table demonstrates a pairing effect;
192
+ > your own attack is the half of it you control. The flip was first seen in an
193
+ > adversarial scratch run (z≈24); section ④ replaces that citation with
194
+ > something a checkout can re-run. The regulation column stayed monotone under
195
+ > every construction tried.
196
+
197
+ **Mechanism**: every save a keeper makes scores the same way —
198
+ `defense + cond/2 + total/3` against the attacker — at three sites: the 1v1
199
+ (`engine.ts:1385` via `getPoint`), the in-match penalty, and the shootout kick
200
+ (both through `takePkShot`, `engine.ts:1251`; `handlePk` calls it in regulation
201
+ too, so keeper defense pays on ~an eighth of goals even in league play). Nothing
202
+ else on the keeper is read by any defensive path, so its pass, dribble and shoot
203
+ are dead points there. The penalty/shootout roll weighs harder because its
204
+ random range is `rand(5)` against the taker's `rand(16)`.
205
+
206
+ **A related trap in the set-piece tables**: `isFkKicker` and `isPkKicker` decide
207
+ free kicks (`engine.ts:785`) and in-match penalties (`engine.ts:1214`), but
208
+ `runPkShootout` ignores both — it sorts the whole squad by `shoot + pass`
209
+ (`engine.ts:1062`). Any set-piece conclusion measured with `gameFlg: 0` therefore
210
+ says nothing about shootouts, and the shootout cannot be steered by nomination at
211
+ all.
212
+
213
+ **That is still not the whole answer**, and the first version of this section
214
+ claimed it was ("defense 10, everything else at 1, total 13 — best in both
215
+ modes"). That claim came from a sweep in which the points freed from the keeper
216
+ were spread evenly over the outfield — nobody lost anything, so no trade-off
217
+ could appear. A sweep has no opportunity cost built in.
218
+
219
+ Give those points a REAL competing use and the ranking changes. The squad has a
220
+ second budget beyond 212: **at most three attributes of value 10 and five of 8–9
221
+ across the whole team** (`validator.ts` `TEN_MAX` / `EIGHT_MAX` — team-wide, not
222
+ per player). Where one of those three 10s goes, measured head to head at 20,000
223
+ matches per pairing on otherwise byte-identical squads:
224
+
225
+ | A single 10 given to… | League | Knockout |
226
+ | --- | --- | --- |
227
+ | the set-piece taker's `shoot` | **1st** | 2nd |
228
+ | an ordinary forward's `shoot` | 2nd | 3rd |
229
+ | the keeper's `defense` | **3rd** | **1st** |
230
+
231
+ | Pairing | League | Knockout |
232
+ | --- | --- | --- |
233
+ | keeper-10 vs taker-10 | 45.1% (z −18.5) | 52.8% (z +7.9) |
234
+ | taker-10 vs forward-10 | 51.9% (z +6.9) | 53.7% (z +10.4) |
235
+ | forward-10 vs keeper-10 | 50.9% (z +3.5) | 43.3% (z −19.1) |
236
+
237
+ *Re-measured 2026-08-20 with SHA-256 seeding: ranking and signs unchanged
238
+ (z −15.5 / +11.0 / +5.5 regulation, +5.4 / +12.2 / −19.1 knockout; mirror
239
+ control z −0.09).*
240
+
241
+ The keeper and the ordinary forward swap ends completely. The set-piece taker is
242
+ first or second in both — the only allocation measured that is never wrong.
243
+
244
+ **Two lessons for this file, learned the hard way on the same day:**
245
+
246
+ 1. A sweep that varies one *named* quantity is not a single-variable sweep if the
247
+ construction ties other quantities to it (the keeper's ratio tied `defense` to
248
+ the total). State the construction, or measure the axes apart.
249
+ 2. **Separating the axes is not enough.** A sweep measures a quantity against
250
+ nothing, so it cannot rank allocations. Any claim of the form "X is optimal"
251
+ needs a control that spends the same budget somewhere else.
252
+
253
+ **Which matches are which** (`packages/api/src`) — still worth knowing, because
254
+ the SIZE of the defense effect differs by an order of magnitude between them:
255
+
256
+ | Match type | `gameFlg` |
257
+ | --- | --- |
258
+ | Ladder (`routes/playoff.ts`) | 0 |
259
+ | Cup group stage (`scheduler/daemon.ts`) | 0 |
260
+ | Cup knockout (`scheduler/daemon.ts`) | 1 |
261
+ | Friendly, default (`routes/matches.ts`) | 1 |
262
+
263
+ ## Strategy round-robin in BOTH modes (`cycle-probe.mts`)
264
+
265
+ Recorded 2026-08-20 — the founding "no cycles" claim had circulated without a
266
+ home in this file. Construction: the SAME squads as the round-robin above (the
267
+ builders are shared code in `squad-lib.mts`, not a copy — an earlier hand-copied
268
+ variant repaired the scarcity caps in a different order and silently built
269
+ different squads from identical slot totals), SHA-256 seeding, mirror control
270
+ z 0.89, eight candidates, 4,000 matches per pairing per mode.
271
+
272
+ Significance is Bonferroni-corrected across all 56 pairings (two modes ×
273
+ 28): |z| >= 3.32 rather than a naive 1.96, which over 56 comparisons all but
274
+ guarantees a few false edges — and one false edge can create or erase a
275
+ dominance or cycle verdict.
276
+
277
+ | | Regulation (draws) | Knockout (ET + shootout) |
278
+ | --- | --- | --- |
279
+ | Significant pairings | 25/28 | 24/28 |
280
+ | 3-cycles (A>B>C>A) | **0** | **0** |
281
+ | Dominant strategy | none | **`role-433`** beats all seven |
282
+
283
+ Mode reversals that survived this re-measurement:
284
+
285
+ - **Attack vs defense lean** (same formation, same budgets): `def-433` beats
286
+ `role-433` 56.5% in regulation; the SAME pairing flips to 53.6% for the
287
+ attack lean in knockouts. Both significant — the cleanest reversal measured.
288
+ - **Formation**: `def-532` beats `bal-442` 73.9% in regulation and loses 56.6%
289
+ in knockouts.
290
+ - **Cheap keeper**: `gkmin-433` collapses in knockouts (27~42% against the
291
+ other top squads). Its regulation edge over the top group did NOT survive the
292
+ corrected builders (50.8% vs `def-433`, 47.4% vs `def-532` — not significant);
293
+ what remains significant in regulation is its edge over `role-433` (57.0%)
294
+ and everything below the top group.
295
+
296
+ Monotone in both modes, still: role differentiation (`flat-433` last
297
+ everywhere) and spreading over star-concentration (`stars-433` next-to-last).
298
+
299
+ Scope: all squads tenure=0, as everywhere in this file. Production applies
300
+ `applyMatchGrowth()` before the engine, raising effective totals — these
301
+ dominance/reversal claims are about zero-buff squads until the buff-0/max
302
+ control runs (docs/tactics/01, track 0.1).
88
303
 
89
304
  ## Home advantage: none
90
305
 
@@ -103,34 +318,89 @@ Same squad, only the kicker slots move. Opponent fixed, 3,000 matches each.
103
318
  | DF / DF | 20.4 | 1.149 |
104
319
  | GK / GK | 17.4 | 1.066 |
105
320
 
106
- Conversion tracks the taker's shooting, so the kicks belong with the best
107
- shooter. Giving both roles to one player costs nothing.
321
+ Conversion tracks the taker's shooting for FREE kicks. This table moved both
322
+ roles together and never crossed the two criteria, so it says nothing about the
323
+ PK slot on its own. `pk-taker.mts` crosses them: one squad held byte-identical,
324
+ only `pkKicker` moved between a 9-pass/9-shoot midfielder (the PK formula's
325
+ pick — `takePkShot` scores the taker as `(pass + shoot) / 2 + …`) and a
326
+ 10-shoot/4-pass striker (the FK criterion's pick). Opponent fixed, regulation,
327
+ 4,000 matches per arm, mirror z −0.62:
328
+
329
+ | pkKicker | (pass+shoot)/2 | PK attempts | Conversion |
330
+ | --- | --- | --- | --- |
331
+ | mid 9/9 | 9 | 299 | **83.9%** |
332
+ | fw 10/4 | 7 | 301 | 69.8% |
333
+
334
+ +14.2pp, two-proportion z 4.12. In-match penalties are rare (~0.07 attempts
335
+ per match here), so the points axis is underpowered — the verdict is the
336
+ conversion rate.
337
+
338
+ The FK half is measured the same way (section ② of the probe): only
339
+ `fkKicker` moves, `pkKicker` pinned. A direct FK (half of `doFk`) scores by
340
+ the kicker's `shoot`; a crossed FK gates on the kicker's `pass` and then a
341
+ different receiver shoots. Attempt counts include foul-site direct FKs taken
342
+ by the fouled ball-carrier — those ignore the flag and are identically
343
+ distributed across both arms, so the BETWEEN-ARM gap is attributable to the
344
+ designated-kicker FKs alone (and the per-attempt gap is an underestimate).
345
+
346
+ | fkKicker | shoot | pass | FK attempts | Team FK goals | Per attempt | W-D-L |
347
+ | --- | --- | --- | --- | --- | --- | --- |
348
+ | mid 9/9 | 9 | 9 | 2,307 | 793 | 34.4% | 1289-1532-1179 |
349
+ | fw 10/4 | 10 | 4 | 2,311 | 1,107 | **47.9%** | 1420-1470-1110 |
350
+
351
+ −13.5pp, two-proportion z −9.34; points 5,399 vs 5,730. Even with the pass
352
+ gate, the shoot-heavy taker wins the FK slot — in THIS fixture, whose
353
+ receivers are flat: the crossed half's payoff runs through the receivers'
354
+ `shoot`, so a squad with strong secondary attackers shifts value back toward
355
+ the crossing taker. Both set pieces roll through `getPoint`/`takePkShot`,
356
+ which add `total/3` (and condition) to the attribute term — the probe held
357
+ totals and condition equal to isolate that term. So: free kicks to the best
358
+ `shoot + total/3`, the penalty to the best `(pass+shoot)/2 + total/3`; with
359
+ unequal condition include `cond/2` too; one player may hold both roles when
360
+ he wins both criteria.
108
361
 
109
362
  ## How many friendlies a conclusion needs
110
363
 
111
364
  Two squads with a known true gap, resampled 20,000 times at each sample size.
112
365
 
366
+ Re-measured 2026-08-20 with SHA-256 seeding (the original numbers came from the
367
+ correlated FNV seeder; shape unchanged, every row a touch weaker). Scope: this
368
+ is ONE matchup — a keeper-10 squad against a keeper-24 squad, ratio-locked
369
+ construction — so the true gap is that specific ~1.8-win-in-ten one. A smaller
370
+ true gap needs more matches than the row says; a larger one, fewer.
371
+
372
+ Scope, part two (Codex P2, #688): **everything below is `gameFlg 0` —
373
+ regulation, draws allowed** (`allowDraw: true` on a friendly), which is what
374
+ `sample-size.mts` simulates. A DEFAULT friendly is `gameFlg 1`. Running the
375
+ probe with `GAME_FLG=1` does not merely shrink the tie column: the same two
376
+ squads' true ordering FLIPS — the keeper-10 squad, the better side in
377
+ regulation, loses 48.5/51.5 over 20,000 default-mode matches, consistent with
378
+ the keeper section's mode dependence (ET and a shootout multiply keeper
379
+ rolls). A "which squad is better" verdict is mode-specific twice over: the
380
+ sample sizes below apply only to draw-allowed friendlies, and the ANSWER can
381
+ change when the mode does. Measure in the mode you intend to play.
382
+
113
383
  | Friendlies | Correct conclusion | Wrong conclusion | Tied |
114
384
  | --- | --- | --- | --- |
115
- | 1 | 35.3% | 16.7% | 48.0% |
116
- | 3 | 53.2% | 19.6% | 27.2% |
117
- | 5 | 61.6% | 18.4% | 20.0% |
118
- | 10 | 73.0% | 14.5% | 12.5% |
119
- | 20 | 85.0% | 8.7% | 6.4% |
120
- | 30 | 90.7% | 5.8% | 3.5% |
121
- | 50 | 96.4% | 2.3% | 1.3% |
122
- | 100 | 99.4% | 0.4% | 0.1% |
385
+ | 1 | 33.6% | 17.2% | 49.2% |
386
+ | 3 | 50.2% | 20.4% | 29.3% |
387
+ | 5 | 58.3% | 19.7% | 22.0% |
388
+ | 10 | 69.7% | 16.6% | 13.7% |
389
+ | 20 | 81.5% | 11.1% | 7.4% |
390
+ | 30 | 88.0% | 7.5% | 4.5% |
391
+ | 50 | 94.1% | 3.9% | 1.9% |
392
+ | 100 | 98.9% | 0.7% | 0.4% |
123
393
 
124
394
  And the false-positive side — **identical** squads, by three different
125
395
  definitions of "one side looks better":
126
396
 
127
397
  | Friendlies | Record not exactly level | Win-loss gap ≥ 2 | Significant (sign test, p<0.05) |
128
398
  | --- | --- | --- | --- |
129
- | 1 | 50.7% | 0.0% | 0.0% |
130
- | 5 | 75.4% | 34.9% | 0.0% |
131
- | 10 | 82.7% | 50.8% | 0.8% |
132
- | 30 | 89.8% | 70.1% | 3.0% |
133
- | 100 | 93.8% | 82.1% | 4.1% |
399
+ | 1 | 51.4% | 0.0% | 0.0% |
400
+ | 5 | 75.8% | 35.0% | 0.0% |
401
+ | 10 | 82.8% | 50.9% | 1.0% |
402
+ | 30 | 90.6% | 72.3% | 2.9% |
403
+ | 100 | 94.6% | 83.9% | 3.5% |
134
404
 
135
405
  **The first column is a trap and was published on its own in an earlier version
136
406
  of this file.** "Not exactly level" approaches 100% for any pair of squads simply
@@ -174,19 +444,90 @@ rest drawn.
174
444
 
175
445
  ## Note for the maintainers
176
446
 
177
- Spending the minimum on the goalkeeper is monotonically better across the whole
178
- range (1.406 vs 0.991 points per match). Anyone who runs a few hundred
179
- simulations will find it, and agents playing through the MCP server will find it
180
- faster than people will.
181
-
182
- It is worth keeping in proportion, though: against a naive squad, role shaping
183
- alone wins 43.6% and adding the cheap keeper takes that to 52.1%. Head to head,
184
- the cheap-keeper version beats the conventional-keeper version 25.7% to 19.3%
185
- with 55% drawn. So it is an edge, not the game. The thing that actually
186
- decides matches is whether players have distinct roles at all.
187
-
188
- `SKILL.md` therefore states the *principle* points spent on the keeper do less
189
- work, because of how the engine aggregates rather than a "set your keeper to
190
- 10" instruction. That is the honest version and leaves the choice with the
191
- player. If the keeper is ever given team contribution or a budget floor, re-run
192
- these probes and rewrite both files.
447
+ > **2026-08-19: this note was rewritten twice in one day.** First it said the
448
+ > cheap keeper was "monotonically better across the whole range". Then it said
449
+ > the direction reverses in knockouts. Both were readings of a sweep that could
450
+ > not separate the keeper's total from its defense. The section above has the
451
+ > separated measurement; this note now describes that.
452
+
453
+ The keeper slot reads exactly one attribute `defense` at exactly three
454
+ sites: the 1v1, the in-match penalty (a regulation event too, ~an eighth of all
455
+ goals), and the shootout kick. Everything else parked there is dead weight. So there
456
+ are two independent facts, and conflating them produced both earlier errors:
457
+
458
+ - **Total: smaller is better in matches that allow draws** that held under
459
+ every construction tried. In knockouts it is OUTFIELD-CONDITIONAL (see the
460
+ warning above): a low-scoring squad reaches shootouts often enough that a
461
+ bigger keeper total pays there.
462
+ - **Defense: higher is better on its own slot, in both modes** — weakly in
463
+ regulation, strongly in knockouts. But a 10 there is one of only three the
464
+ whole team may hold, and in the scarce-10 comparison above the keeper's 10
465
+ ranked LAST in league play.
466
+
467
+ So even the two facts do not compose into one recommended shape. What survives
468
+ is the negative advice: never leave points in the keeper's pass/dribble/shoot,
469
+ and never scale a balanced keeper down.
470
+
471
+ Keep it in proportion. Against a naive squad, role shaping alone wins 43.6% and
472
+ adding a cheap keeper takes that to 52.1%. Role differentiation still decides
473
+ more matches than the keeper does.
474
+
475
+ `SKILL.md` states the two facts separately and warns against scaling a balanced
476
+ keeper down, because that drags defense along with the total. If the keeper is
477
+ ever given team contribution or a budget floor, re-run these probes in BOTH
478
+ modes AND with the two axes separated — the ratio-locked sweep alone will not
479
+ show it.
480
+
481
+ ### The decision, and what would reopen it
482
+
483
+ Issue #604 settled on **leaving the engine faithful to sgsoccer** — and made
484
+ that settlement *conditional* on the meta not collapsing onto one answer. The
485
+ separated measurement makes that risk concrete again: defense 10 is the single
486
+ best keeper defense everywhere measured, and under ordinary shootout exposure —
487
+ regulation, and knockout pairings that reach shootouts at typical rates — the
488
+ best shape is defense 10, total 13. The one measured exception is the pairing
489
+ block above: against shoot-starved pairings reaching shootouts ~75% of the
490
+ time, the optimum climbs the total axis (total 29 beat total 13 by ~0.85 ppm)
491
+ while defense stays at 10. So what a converging population would collapse onto
492
+ is per-context on the total axis but single-valued on the defense axis. The
493
+ earlier hope that "the meta has nowhere to collapse to" rested on a reversal
494
+ that turned out to be an artefact.
495
+
496
+ What is worth watching therefore returns to the original question, with one
497
+ correction: **watch the keeper's `defense`, not its total.** A population
498
+ converging on total 13 while keeping defense at 10 has found the optimum; a
499
+ population converging on a low total with a scaled-down defense has been misled
500
+ and is playing worse. Those two look identical to a total-only metric.
501
+
502
+ Read-only from production on 2026-08-18 (32 human squads, seeded AI bots
503
+ excluded): median keeper total **19**, mean 17.6, **nobody** above 24, and only
504
+ two squads (6%) at the minimum. So the direction the probes predict is visible —
505
+ heavy keepers are unbought — but the meta has NOT converged. Two thirds sit at
506
+ 19–20, which is the default left untouched rather than a choice.
507
+
508
+ That reading is limited by sample, not by engine design: in that season only two
509
+ human teams had played more than three matches, so no comparison *by result* was
510
+ possible. The re-review triggers therefore key on sample first:
511
+
512
+ | # | Trigger | Threshold |
513
+ | --- | --- | --- |
514
+ | 1 | Human teams with enough matches to compare by result — AND a contrast: two keeper-total cohorts of >= 3 qualified teams each. UNDECIDABLE (reported, never firing) until matches record the squad they were played with: cohorts group season records by the CURRENT keeper, and FA-market trading makes mid-season keeper edits routine, so a cohort can be fabricated | >= 10 teams, >= 2 cohorts, per-match squad snapshots |
515
+ | 2 | Share of human squads on the MONITORED allocation — total <= 13 (the legal minimum with defense 10 is 1+1+1+10) AND defense >= 9. Cheap-but-weak squads are the opposite event (stale-advice damage) and are reported separately as `misled`, never firing this trigger. Undecidable below 10 human squads — a share over fewer reads one manager's choice as a meta | > 40% of >= 10 squads |
516
+ | 3 | Median keeper total of the top 10 ranked human teams, each with >= 10 matches (a one-match post-rollover ranking stays null). UNDECIDABLE (reported, never firing) until per-match squad snapshots exist — rank is season-long, the keeper total is current, and leaders swapping keepers after earning their records would fire it over zero matches at the new total | <= 14, plus squad snapshots |
517
+
518
+ These live as code, not as prose here — prose does not get re-read:
519
+
520
+ ```bash
521
+ DATABASE_URL=postgres://… pnpm --filter @sws26/api exec tsx scripts/gk-budget-watch.ts
522
+ ```
523
+
524
+ It prints the current distribution, the record grouped by keeper total, and
525
+ whether any trigger has fired. `packages/api/src/lib/gk-budget-watch.ts` holds
526
+ the thresholds; the CLI reads squads through `TeamStore` rather than the
527
+ `players` table, because a sold keeper leaves `roster_slots` without rewriting
528
+ the legacy row.
529
+
530
+ If a trigger fires, re-run the probes above BEFORE touching anything, then
531
+ decide between the options in #604. Whatever is chosen, the engine change and
532
+ both documents move in the SAME change — `SKILL.md` is what agents believe, and
533
+ a stale sentence there makes them exclude squads that are perfectly legal.