pog-mcp 0.9.18 → 0.9.19

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.9.18",
3
+ "version": "0.9.19",
4
4
  "type": "module",
5
5
  "description": "MCP server that lets an AI agent play Proof of Goal — wallet, sign-in, squad building, and matches as typed tools.",
6
6
  "license": "MIT",
@@ -4991,9 +4991,14 @@ engine formula: `getPoint`/`takePkShot` show both use `atkSkill + cond/2 +
4991
4991
  total/3 + roll`, and FK's single `fkKicker` flag triggers a 50/50 mix of a
4992
4992
  shoot-based and a pass-based branch, so the expected-value-optimal ranking
4993
4993
  for BOTH roles is `(pass+shoot)/2 + total/3` — with a tie-break that keeps
4994
- the two roles on different players. This still omits `ofTeamContrib`, a
4995
- team-aggregate term `getPoint` also adds — round 8's Finding 1, deferred
4996
- (see "What Codex found").
4994
+ the two roles on different players. This omitted `ofTeamContrib`, a
4995
+ team-aggregate term `getPoint` also adds — round 8's Finding 2, deferred at
4996
+ the time (see "What Codex found") and later closed via issue #852: PK needs
4997
+ no correction (`takePkShot` bypasses `getPoint` entirely), and FK's ranking
4998
+ now adds `0.5*ofTeamContribDirect` (only the direct/shoot branch reaches a
4999
+ nonzero team-aggregate term; the crossed/pass branch's rate is 0 for every
5000
+ position). Closing it flipped `set_piece`'s own monotone classification —
5001
+ see Result and "What Codex found" below.
4997
5002
 
4998
5003
  ### Method
4999
5004
 
@@ -5026,19 +5031,25 @@ loop structure.
5026
5031
  | `press` | yes | 7/8 | 8/8 | 39.88 | 2/8 | **7/8** |
5027
5032
  | `build` | yes | 7/8 | 8/8 | 12.52 | 1/8 | 0/8 |
5028
5033
  | `keeper` | yes | 8/8 | 8/8 | 19.02 | **0/8** | **0/8** |
5029
- | `set_piece` | yes | 8/8 | 8/8 | 11.38 | 4/8 | 0/8 |
5034
+ | `set_piece` | yes | 8/8 | 8/8 | 11.38 | **0/8** | 0/8 |
5030
5035
 
5031
5036
  All six adopted (judged on the any-opponent column, matching the code's
5032
- adoption rule); `line`/`press`/`build`/`set_piece` non-monotone,
5033
- `balance`/`keeper` cleanly monotone. G0 passes (4 of 6 non-monotone, above
5034
- the 2-required bar). `press` remains the strongest trade-off: 7 of 8
5035
- core×mode cells have a supported leader that flips across the 4-opponent
5036
- grid. `set_piece`'s `flat` core is no longer an exact 0.00pp tie (as in
5037
- round 4) — it now shows a small but genuinely significant effect
5038
- (+0.29/+0.33pp) once the ranking criterion includes `total/3`, which makes
5039
- `slotTotals()`'s rounding-driven total differences the real tiebreaker
5040
- instead of array order. Round 6 additionally found FK's ranking was missing
5041
- half its own mechanic (see "What Codex found").
5037
+ adoption rule); `line`/`press`/`build` non-monotone, `balance`/`keeper`/
5038
+ `set_piece` cleanly monotone. G0 passes (3 of 6 non-monotone, above the
5039
+ 2-required bar). `press` remains the strongest trade-off: 7 of 8 core×mode
5040
+ cells have a supported leader that flips across the 4-opponent grid.
5041
+ `set_piece`'s non-monotone count in this table (0/8) reflects the #852 fix
5042
+ below, not the original round-8 measurement (4/8, `flat`×2 + `gkmin`×2) —
5043
+ closing the `ofTeamContrib` gap moved `high`'s FK pick on those two cores
5044
+ onto a candidate matching `mid`'s own default FK kicker (same position and
5045
+ attributes); the accompanying PK-tiebreak shift measures indistinguishably
5046
+ from `mid` too, though for a more specific reason than "identical kickers"
5047
+ (see the round-3 correction below) — together removing the dip that made
5048
+ those two cores' ladders non-monotone. `shaped`/`gkheavy` are byte-identical to the
5049
+ pre-#852 numbers — their position groups aren't attribute-uniform, so the
5050
+ added team-aggregate term didn't move the top-ranked candidate. Round 6
5051
+ additionally found FK's ranking was missing half its own mechanic (see
5052
+ "What Codex found").
5042
5053
 
5043
5054
  `D_*_APPR` (#745's three questions): `focus-total` is now restricted to
5044
5055
  FORWARDS specifically (round 7 — searching all outfielders let a DF win the
@@ -5131,7 +5142,11 @@ still-colliding FK/PK selection, proportional instead of concentrated
5131
5142
  which is the correct consequence of the engine's real mechanics, not a
5132
5143
  bug — it just means round 4's "PK falls to the next-best candidate when
5133
5144
  it ties with FK's pick" tie-break now fires on nearly every ladder cell
5134
- instead of occasionally.
5145
+ instead of occasionally. **This held only through round 8** — issue #852
5146
+ (below) later added a term to FK's formula alone, so FK and PK diverge
5147
+ again and the tiebreak fires only when they happen to coincide (on
5148
+ `flat`/`gkmin` specifically, it no longer fires at all — see #852's own
5149
+ entry for the mechanics).
5135
5150
 
5136
5151
  **Round 7 (1 P1, 4 P2)** — new defects inside earlier rounds' fixes:
5137
5152
 
@@ -5182,10 +5197,10 @@ still-colliding FK/PK selection, proportional instead of concentrated
5182
5197
  −8.43→−5.00pp) — the axis's own max \|Δ\| drops from 32.20pp to 22.87pp.
5183
5198
  Non-monotone/opponent-conditional classification is unchanged (1/8, 3/8) —
5184
5199
  only the magnitude was wrong, not the qualitative call.
5185
- - **[Deferred to a follow-up issue]** `set_piece`'s FK/PK ranking
5186
- (`(shoot+pass)/2 + total/3`, fixed in rounds 5–6) omits `ofTeamContrib`,
5187
- the team-aggregate term `getPoint`'s full formula also adds:
5188
- `ofPoint = ofPlayerP + ofTeamContrib`, where `ofTeamContrib =
5200
+ - **[Deferred to a follow-up issue, later closed — see below]** `set_piece`'s
5201
+ FK/PK ranking (`(shoot+pass)/2 + total/3`, fixed in rounds 5–6) omitted
5202
+ `ofTeamContrib`, the team-aggregate term `getPoint`'s full formula also
5203
+ adds: `ofPoint = ofPlayerP + ofTeamContrib`, where `ofTeamContrib =
5189
5204
  (atkCondSum*atkRate*0.075 + atkAttrSum*atkRate*0.1) * (teamPow/100)`.
5190
5205
  Codex supplied a concrete counterexample on the `flat` core where including
5191
5206
  this term could flip the top-ranked candidate. Closing this gap requires
@@ -5194,14 +5209,99 @@ still-colliding FK/PK selection, proportional instead of concentrated
5194
5209
  Per the coordinator's round-7 scope-management guidance, this was left as a
5195
5210
  documented "KNOWN LIMITATION" code comment rather than extending the review
5196
5211
  loop further, and tracked in a separate GitHub issue (linked from PR #840)
5197
- instead. It does not affect `set_piece`'s own adopted/non-monotone verdict —
5198
- the current ranking already captures the dominant term of the real
5199
- mechanism; only a secondary correction term is missing.
5212
+ instead. **At the time, this was believed not to affect `set_piece`'s own
5213
+ adopted/non-monotone verdict — the current ranking already captures the
5214
+ dominant term of the real mechanism; only a secondary correction term is
5215
+ missing.** That belief turned out to be wrong.
5216
+
5217
+ **Issue #852 (post-#809) — closed the deferred gap, and it was not merely
5218
+ "secondary":** reading `engine.ts` directly settled the exact formula each
5219
+ role needs. `takePkShot` bypasses `getPoint` entirely (its own comment says
5220
+ so); its formula is `(pass+shoot)/2 + cond/2 + total/3 + roll`, which
5221
+ `pkRanking` (`(pass+shoot)/2 + total/3`) matches in RELATIVE ORDER — not
5222
+ term-for-term, since it omits `cond/2` and the roll — under this probe's own
5223
+ uniform-`cond:5` invariant (now runtime-asserted, not just assumed). PK
5224
+ needs no further correction. FK's two branches reach `ofTeamContrib` asymmetrically:
5225
+ `getPoint`'s `effScene = (skillType==='shoot' || scene===SCENE.A0) ? 0 :
5226
+ scene` forces `doFkDirect` (`skillType:'shoot'`) to always read the A0
5227
+ column of `O_*_RATE` (FW 1.0 / OMF 0.5 / DMF 0.2 / DF 0.1, transcribed
5228
+ verbatim from the engine source) regardless of `SCENE.FK1`'s own numeric
5229
+ value, while `doFkCross` (`skillType:'pass'`, `scene: SCENE.FK2`) reads
5230
+ `O_*_RATE[9]` — 0.0 for every position, confirmed by reading all four
5231
+ arrays. `reassignKicker()` now adds `0.5*ofTeamContribDirect(p)` (half-
5232
+ weighted, matching the 50/50 direct/cross split) to the FK score only.
5233
+ Every squad this probe builds has uniform `cond:5` and sums to exactly 212
5234
+ (`squad-lib.mts`'s `TOTAL`), so `ofTeamContribDirect` is derived from those
5235
+ values rather than hardcoded, keeping the formula self-evidently correct.
5236
+ Re-running `N=20,000` end to end (14,066,000 matches, same loop structure,
5237
+ self-check passed) confirmed Codex's counterexample was real: on `flat`
5238
+ and `gkmin`, the top **FK** candidate actually changes from a marginally
5239
+ higher-total DF/OMF to a lower-total FW, because `O_FW_RATE[0]` (1.0) is
5240
+ 10× `O_DF_RATE[0]` (0.1) — enough to overturn the old ranking's tiny
5241
+ `total/3`-driven tiebreak. That FW matches `mid`'s own default FK kicker
5242
+ (slot 9) in both position and attributes.
5243
+
5244
+ **PK is a separate story — `pkRanking` itself is unchanged by this fix**
5245
+ [round-3 review correction]. `ofTeamContribDirect` was added to `fkScore`
5246
+ only; `pkRanking` still ranks by the bare `(pass+shoot)/2+total/3`. What
5247
+ changes is round 4's tiebreak condition (`pkRanking[0] === fkTarget`): under
5248
+ the OLD formula, `fkTarget` coincided with `pkRanking`'s own top pick (both
5249
+ picked the same highest-`total` candidate), so the tiebreak fired and PK
5250
+ fell to `pkRanking[1]`. Under the NEW formula `fkTarget` moves to a
5251
+ different candidate (the promoted FW), so the tiebreak no longer fires and
5252
+ PK now gets `pkRanking[0]` directly — a candidate that was previously
5253
+ pushed down to second place. The two test cores land differently: on `flat`,
5254
+ `pkRanking[0]` is the 20-total DF, so `high`'s PK kicker is now a 20-total
5255
+ DF — genuinely DIFFERENT from `mid`'s 19-total FW PK kicker in both
5256
+ position and total (not "identical" — the 1-point total gap is just small
5257
+ enough that `high%`≈`mid%` at this sample size). On `gkmin`, `pkRanking[0]`
5258
+ is an OMF whose `(shoot+pass)/2` (6, from a 6/6 split) happens to exactly
5259
+ equal `mid`'s FW PK kicker's `(shoot+pass)/2` (also 6, from a 10/2 split) —
5260
+ different position, same average, so `pkScore` itself is identical between
5261
+ them, which is the real reason `high` and `mid` measure indistinguishably
5262
+ there. `shaped`/`gkheavy` are byte-identical before/after: their position
5263
+ groups are non-uniform, so the added term didn't reach far enough to flip
5264
+ the top FK candidate there (and the PK tiebreak dynamics above never
5265
+ trigger). Net
5266
+ effect: `set_piece`'s non-monotone count dropped from 4/8 to 0/8 —
5267
+ `set_piece` reclassifies from "adopted, non-monotone" to "adopted,
5268
+ monotone," joining `balance`/`keeper`. This does NOT change G0's own
5269
+ pass/fail verdict (non-monotone axis count: 4/6→3/6, still above the
5270
+ 2-required bar) — but it does mean the deferral's own "does not affect the
5271
+ verdict" reasoning was incomplete: a secondary correction term changed a
5272
+ qualitative classification, not just a number.
5273
+
5274
+ **Remaining limitation (PR #858 review, tracked in issue #859) — the cross
5275
+ branch still scores the wrong player.** `fkScore`'s 0.5-weighted cross
5276
+ contribution still credits the KICKER's own `shoot`/`ofTeamContribDirect`,
5277
+ but `doFkCross`'s `crossPoints` (`getPoint(..., 'pass', SCENE.FK2, ...)`) is
5278
+ only a binary gate — it never reads `shoot`, and its `ofTeamContrib` is
5279
+ provably zero (`O_*_RATE[9]` is 0.0 for every position). If the gate
5280
+ succeeds, the actual shot is taken by a SEPARATELY weight-drawn receiver
5281
+ (`getPlayer(atk, SCENE.A1, true, kickerIdx)` — excludes the kicker, weighted
5282
+ by `O_*_APPR[SCENE.A1]`: OMF 100/FW 50/DMF 30/DF 20), scored by their own
5283
+ stats. Promoting a candidate to `fkKicker` therefore has an unmodeled
5284
+ second-order cost: removing them from the cross branch's receiver pool —
5285
+ exactly what the `flat`/`gkmin` FW promotions above do. Closing this needs
5286
+ the receiver draw's expected value (tractable — a weighted average, reusing
5287
+ `ofTeamContribDirect`) AND the gate's success probability, which is NOT
5288
+ context-free: it depends on which specific opponent defender contests
5289
+ `SCENE.FK2` that match, a value this ranking (evaluated once per `Candidate`,
5290
+ before any opponent is chosen) cannot know — unlike the 4-opponent grid this
5291
+ probe deliberately measures against elsewhere. A genuinely deeper,
5292
+ opponent-dependent piece of engine-mechanics research than #852's own gap,
5293
+ tracked separately rather than folded into this fix. The measured numbers
5294
+ above are still valid (they come from real simulations); the claim that this
5295
+ formula's picks are the true optimum is exactly as caveated as this gap.
5200
5296
 
5201
5297
  ### What it means
5202
5298
 
5203
- - **G0 passes.** 6/6 adopted, 4/6 non-monotone. `docs/pog/02-axes-measurement.md`
5204
- has the full write-up and all eight review rounds' changelogs.
5299
+ - **G0 passes.** 6/6 adopted, 3/6 non-monotone (originally reported as 4/6 —
5300
+ see issue #852 above: closing `set_piece`'s `ofTeamContrib` gap
5301
+ reclassified it from non-monotone to monotone; the pass/fail verdict
5302
+ itself is unaffected, still comfortably above the 2-required bar).
5303
+ `docs/pog/02-axes-measurement.md` has the full write-up and all eight
5304
+ review rounds' changelogs.
5205
5305
  - **The realizability column's direction is explicitly unresolved, not
5206
5306
  presented as a clean lower bound** — round 7 found a second limitation
5207
5307
  (out-of-range synthetic reserves) pulling the opposite way from round 5's
@@ -5222,4 +5322,6 @@ still-colliding FK/PK selection, proportional instead of concentrated
5222
5322
  is unchanged so the match count is identical, only wall-clock varies between
5223
5323
  runs; self-checked against the loop structure). The default `N=6,000`
5224
5324
  reaches the same G0 PASS verdict in about a third of the time. Whole run:
5225
- `pog-axes.mts`.
5325
+ `pog-axes.mts`. Re-run post-#852 (same `N=20,000`, same loop structure):
5326
+ identical 14,066,000 matches in 151.6s — wall-clock varies, the match count
5327
+ does not.