@knights-eye-chess/ai-analysis 0.0.0-stage → 0.1.0-dev.17

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.
Files changed (258) hide show
  1. package/LICENSE +235 -0
  2. package/LICENSING.md +26 -0
  3. package/NOTICE +5 -0
  4. package/NPM-PUBLISHING.md +28 -0
  5. package/README.md +139 -2
  6. package/SOURCE-ORIGIN.json +16 -0
  7. package/THIRD_PARTY_NOTICES.md +18 -0
  8. package/build.mjs +22 -0
  9. package/dist/answer-pipeline.test.mjs +375 -0
  10. package/dist/chunk-PLKNJRPF.js +10662 -0
  11. package/dist/chunk-XQDZJOYD.js +2515 -0
  12. package/dist/index.js +146 -0
  13. package/dist/owner-tests/chess-reasoning-compact-search-preparation.test.mjs +963 -0
  14. package/dist/owner-tests/mechanism-encoding.test.mjs +529 -0
  15. package/dist/owner-tests/move-mechanism-facts.test.mjs +396 -0
  16. package/dist/owner-tests/pipeline-host-preparation.test.mjs +10556 -0
  17. package/dist/owner-tests/position-preparation.test.mjs +625 -0
  18. package/dist/owner-tests/preparation-consolidation.test.mjs +1052 -0
  19. package/dist/owner-tests/prose-reference-admission.test.mjs +1770 -0
  20. package/dist/owner-tests/score-comparison-notices.test.mjs +240 -0
  21. package/dist/owner-tests/simple-engine-presentation.test.mjs +233 -0
  22. package/dist/owner-tests/tool-input-loop-guard.test.mjs +78 -0
  23. package/dist/research.js +2274 -0
  24. package/dist/runtime-closure.json +103 -0
  25. package/dist/shared-runtime-closure.json +39 -0
  26. package/dist/shared.js +152 -0
  27. package/dist/types/ai-request-failure.d.ts +14 -0
  28. package/dist/types/ai-request-limits.d.ts +4 -0
  29. package/dist/types/ai-routing-diagnostics.d.ts +14 -0
  30. package/dist/types/chess-reasoning/ablation-adaptive-evidence-loop.d.ts +127 -0
  31. package/dist/types/chess-reasoning/ablation-adaptive-runtime.d.ts +178 -0
  32. package/dist/types/chess-reasoning/ablation-direct-synthesis.d.ts +1030 -0
  33. package/dist/types/chess-reasoning/ablation-evidence-bundle.d.ts +185 -0
  34. package/dist/types/chess-reasoning/ablation-evidence-capture.d.ts +37 -0
  35. package/dist/types/chess-reasoning/ablation-lean-direct-runtime.d.ts +193 -0
  36. package/dist/types/chess-reasoning/ablation-model-facing-evidence.d.ts +38 -0
  37. package/dist/types/chess-reasoning/ablation-transport-correction.d.ts +13 -0
  38. package/dist/types/chess-reasoning/ai-sdk-runtime-support.d.ts +93 -0
  39. package/dist/types/chess-reasoning/answer-host-contracts.d.ts +59 -0
  40. package/dist/types/chess-reasoning/answer-model-connector.d.ts +68 -0
  41. package/dist/types/chess-reasoning/answer-pipeline-exports.d.ts +98 -0
  42. package/dist/types/chess-reasoning/answer-pipeline.d.ts +41 -0
  43. package/dist/types/chess-reasoning/answer-reference-validation.d.ts +40 -0
  44. package/dist/types/chess-reasoning/answer-tool-host.d.ts +8 -0
  45. package/dist/types/chess-reasoning/branch-expansion-policy.d.ts +13 -0
  46. package/dist/types/chess-reasoning/branch-expansion.d.ts +39 -0
  47. package/dist/types/chess-reasoning/causal-claim-checks.d.ts +21 -0
  48. package/dist/types/chess-reasoning/chess-reference-dictionary.d.ts +13 -0
  49. package/dist/types/chess-reasoning/classification-question-target.d.ts +22 -0
  50. package/dist/types/chess-reasoning/classification-reassessment.d.ts +31 -0
  51. package/dist/types/chess-reasoning/compact-search-preparation.d.ts +7 -0
  52. package/dist/types/chess-reasoning/concept-registry.d.ts +99 -0
  53. package/dist/types/chess-reasoning/conversation-context.d.ts +12 -0
  54. package/dist/types/chess-reasoning/engine-search-failure.d.ts +7 -0
  55. package/dist/types/chess-reasoning/evidence-citation-normalization.d.ts +9 -0
  56. package/dist/types/chess-reasoning/evidence-licenses.d.ts +57 -0
  57. package/dist/types/chess-reasoning/evidence-selector-contract.d.ts +5 -0
  58. package/dist/types/chess-reasoning/existing-analysis-context.d.ts +104 -0
  59. package/dist/types/chess-reasoning/fact-observation-provider.d.ts +15 -0
  60. package/dist/types/chess-reasoning/fact-provider.d.ts +111 -0
  61. package/dist/types/chess-reasoning/fresh-engine-pv-witnesses.d.ts +67 -0
  62. package/dist/types/chess-reasoning/historical-prose.d.ts +23 -0
  63. package/dist/types/chess-reasoning/idea-graph.d.ts +34 -0
  64. package/dist/types/chess-reasoning/initial-search-plan.d.ts +37 -0
  65. package/dist/types/chess-reasoning/lossless-evidence-sharing.d.ts +18 -0
  66. package/dist/types/chess-reasoning/matched-comparison.d.ts +10 -0
  67. package/dist/types/chess-reasoning/material-accounting.d.ts +80 -0
  68. package/dist/types/chess-reasoning/material-format.d.ts +4 -0
  69. package/dist/types/chess-reasoning/mechanical-fact-provider.d.ts +33 -0
  70. package/dist/types/chess-reasoning/mechanical-ray-facts.d.ts +19 -0
  71. package/dist/types/chess-reasoning/mechanism-presentation.d.ts +9 -0
  72. package/dist/types/chess-reasoning/move-mechanism-facts.d.ts +45 -0
  73. package/dist/types/chess-reasoning/native-answer-connector.d.ts +4 -0
  74. package/dist/types/chess-reasoning/opening-source-contracts.d.ts +54 -0
  75. package/dist/types/chess-reasoning/opening-source-credits.d.ts +16 -0
  76. package/dist/types/chess-reasoning/opponent-threat-preparation.d.ts +7 -0
  77. package/dist/types/chess-reasoning/piece-identities.d.ts +11 -0
  78. package/dist/types/chess-reasoning/poc-contracts.d.ts +1026 -0
  79. package/dist/types/chess-reasoning/poc-hypotheses.d.ts +74 -0
  80. package/dist/types/chess-reasoning/poc-move-witnesses.d.ts +13 -0
  81. package/dist/types/chess-reasoning/poc-observations.d.ts +23 -0
  82. package/dist/types/chess-reasoning/poc-piece-refs.d.ts +12 -0
  83. package/dist/types/chess-reasoning/poc-plan-model.d.ts +45 -0
  84. package/dist/types/chess-reasoning/poc-semantic-text.d.ts +8 -0
  85. package/dist/types/chess-reasoning/poc-tools.d.ts +81 -0
  86. package/dist/types/chess-reasoning/portable-answer-runtime.d.ts +291 -0
  87. package/dist/types/chess-reasoning/position-analysis-contracts.d.ts +31 -0
  88. package/dist/types/chess-reasoning/position-analysis-scope.d.ts +3 -0
  89. package/dist/types/chess-reasoning/preparation-consolidation.d.ts +21 -0
  90. package/dist/types/chess-reasoning/production-observation-provider.d.ts +16 -0
  91. package/dist/types/chess-reasoning/question-routing.d.ts +326 -0
  92. package/dist/types/chess-reasoning/recorded-main-line.d.ts +37 -0
  93. package/dist/types/chess-reasoning/recorded-position-tools.d.ts +11 -0
  94. package/dist/types/chess-reasoning/recorded-reference-context.d.ts +13 -0
  95. package/dist/types/chess-reasoning/recorded-selected-classification.d.ts +36 -0
  96. package/dist/types/chess-reasoning/reference-limits.d.ts +2 -0
  97. package/dist/types/chess-reasoning/research-exports.d.ts +197 -0
  98. package/dist/types/chess-reasoning/saved-score-presentation.d.ts +14 -0
  99. package/dist/types/chess-reasoning/score-comparison-notices.d.ts +33 -0
  100. package/dist/types/chess-reasoning/server-simple-policy.d.ts +31 -0
  101. package/dist/types/chess-reasoning/simple-agent-config.d.ts +73 -0
  102. package/dist/types/chess-reasoning/simple-board-analysis.d.ts +85 -0
  103. package/dist/types/chess-reasoning/simple-compact-evidence.d.ts +7 -0
  104. package/dist/types/chess-reasoning/simple-completion.d.ts +23 -0
  105. package/dist/types/chess-reasoning/simple-engine-presentation.d.ts +3 -0
  106. package/dist/types/chess-reasoning/simple-evidence-preparation.d.ts +17 -0
  107. package/dist/types/chess-reasoning/simple-evidence-view.d.ts +17 -0
  108. package/dist/types/chess-reasoning/simple-explanation-coverage.d.ts +90 -0
  109. package/dist/types/chess-reasoning/simple-fact-delivery.d.ts +43 -0
  110. package/dist/types/chess-reasoning/simple-fallback-premise.d.ts +5 -0
  111. package/dist/types/chess-reasoning/simple-prose-references.d.ts +108 -0
  112. package/dist/types/chess-reasoning/simple-runtime-version.d.ts +1 -0
  113. package/dist/types/chess-reasoning/stable.d.ts +2 -0
  114. package/dist/types/chess-reasoning/tool-input-loop-guard.d.ts +5 -0
  115. package/dist/types/chess-reasoning/types.d.ts +180 -0
  116. package/dist/types/chess-reasoning/visible-support-lines.d.ts +12 -0
  117. package/dist/types/chess-reasoning/wikibooks-index.d.ts +30 -0
  118. package/dist/types/chess-reasoning/wikibooks-tool-context.d.ts +10 -0
  119. package/dist/types/player-perspective.d.ts +16 -0
  120. package/dist/types/shared-exports.d.ts +22 -0
  121. package/docs/API.md +82 -0
  122. package/docs/OWNERSHIP-ADOPTION.md +35 -0
  123. package/docs/OWNERSHIP-DRIFT.json +55 -0
  124. package/docs/RESEARCH-API.md +28 -0
  125. package/licenses/third-party/INVENTORY.json +202 -0
  126. package/licenses/third-party/ai-7.0.85/LICENSE +13 -0
  127. package/licenses/third-party/ai-sdk--gateway-4.0.69/LICENSE +13 -0
  128. package/licenses/third-party/ai-sdk--provider-4.0.9/LICENSE +13 -0
  129. package/licenses/third-party/ai-sdk--provider-utils-5.0.34/APACHE-2.0.txt +73 -0
  130. package/licenses/third-party/ai-sdk--provider-utils-5.0.34/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
  131. package/licenses/third-party/chess.js-1.4.0/LICENSE +23 -0
  132. package/licenses/third-party/eventsource-parser-3.1.1/LICENSE +21 -0
  133. package/licenses/third-party/json-schema-0.4.0/LICENSE +195 -0
  134. package/licenses/third-party/standard-schema--spec-1.1.0/LICENSE +21 -0
  135. package/licenses/third-party/types--json-schema-7.0.15/LICENSE +21 -0
  136. package/licenses/third-party/types--node-22.19.19/LICENSE +21 -0
  137. package/licenses/third-party/undici-7.30.0/LICENSE +21 -0
  138. package/licenses/third-party/undici-7.30.0/lib/web/fetch/LICENSE +21 -0
  139. package/licenses/third-party/undici-types-6.21.0/LICENSE +21 -0
  140. package/licenses/third-party/vercel--oidc-3.2.0/LICENSE +202 -0
  141. package/licenses/third-party/workflow--serde-4.1.0/APACHE-2.0.txt +73 -0
  142. package/licenses/third-party/workflow--serde-4.1.0/LICENSE.md +3 -0
  143. package/licenses/third-party/zod-4.6.5/LICENSE +21 -0
  144. package/npm-release.json +5 -0
  145. package/package.json +72 -4
  146. package/scripts/check-release-tooling.mjs +22 -0
  147. package/scripts/pack.mjs +68 -0
  148. package/scripts/publish-reviewed.mjs +85 -0
  149. package/scripts/release-tooling.json +13 -0
  150. package/scripts/update-dependency.mjs +44 -0
  151. package/scripts/verify-release.mjs +52 -0
  152. package/src/ai-request-failure.ts +34 -0
  153. package/src/ai-request-limits.ts +4 -0
  154. package/src/ai-routing-diagnostics.ts +33 -0
  155. package/src/chess-reasoning/ablation-adaptive-evidence-loop.ts +515 -0
  156. package/src/chess-reasoning/ablation-adaptive-runtime.ts +924 -0
  157. package/src/chess-reasoning/ablation-direct-synthesis.ts +1128 -0
  158. package/src/chess-reasoning/ablation-evidence-bundle.ts +726 -0
  159. package/src/chess-reasoning/ablation-evidence-capture.ts +513 -0
  160. package/src/chess-reasoning/ablation-lean-direct-runtime.ts +670 -0
  161. package/src/chess-reasoning/ablation-model-facing-evidence.ts +212 -0
  162. package/src/chess-reasoning/ablation-transport-correction.ts +106 -0
  163. package/src/chess-reasoning/ai-sdk-runtime-support.ts +381 -0
  164. package/src/chess-reasoning/answer-host-contracts.ts +10 -0
  165. package/src/chess-reasoning/answer-model-connector.ts +83 -0
  166. package/src/chess-reasoning/answer-pipeline-exports.ts +98 -0
  167. package/src/chess-reasoning/answer-pipeline.ts +195 -0
  168. package/src/chess-reasoning/answer-reference-validation.ts +124 -0
  169. package/src/chess-reasoning/answer-tool-host.ts +10 -0
  170. package/src/chess-reasoning/branch-expansion-policy.ts +32 -0
  171. package/src/chess-reasoning/branch-expansion.ts +80 -0
  172. package/src/chess-reasoning/causal-claim-checks.ts +256 -0
  173. package/src/chess-reasoning/chess-reference-dictionary.ts +21 -0
  174. package/src/chess-reasoning/classification-question-target.ts +30 -0
  175. package/src/chess-reasoning/classification-reassessment.ts +34 -0
  176. package/src/chess-reasoning/compact-search-preparation.ts +125 -0
  177. package/src/chess-reasoning/concept-registry.ts +275 -0
  178. package/src/chess-reasoning/conversation-context.ts +29 -0
  179. package/src/chess-reasoning/engine-search-failure.ts +11 -0
  180. package/src/chess-reasoning/evidence-citation-normalization.ts +15 -0
  181. package/src/chess-reasoning/evidence-licenses.ts +166 -0
  182. package/src/chess-reasoning/evidence-selector-contract.ts +5 -0
  183. package/src/chess-reasoning/existing-analysis-context.ts +544 -0
  184. package/src/chess-reasoning/fact-observation-provider.ts +134 -0
  185. package/src/chess-reasoning/fact-provider.ts +250 -0
  186. package/src/chess-reasoning/fresh-engine-pv-witnesses.ts +128 -0
  187. package/src/chess-reasoning/historical-prose.ts +116 -0
  188. package/src/chess-reasoning/idea-graph.ts +224 -0
  189. package/src/chess-reasoning/initial-search-plan.ts +115 -0
  190. package/src/chess-reasoning/lossless-evidence-sharing.ts +135 -0
  191. package/src/chess-reasoning/matched-comparison.ts +40 -0
  192. package/src/chess-reasoning/material-accounting.ts +55 -0
  193. package/src/chess-reasoning/material-format.ts +47 -0
  194. package/src/chess-reasoning/mechanical-fact-provider.ts +1602 -0
  195. package/src/chess-reasoning/mechanical-ray-facts.ts +123 -0
  196. package/src/chess-reasoning/mechanism-presentation.ts +94 -0
  197. package/src/chess-reasoning/move-mechanism-facts.ts +185 -0
  198. package/src/chess-reasoning/native-answer-connector.ts +13 -0
  199. package/src/chess-reasoning/opening-source-contracts.ts +47 -0
  200. package/src/chess-reasoning/opening-source-credits.ts +53 -0
  201. package/src/chess-reasoning/opponent-threat-preparation.ts +98 -0
  202. package/src/chess-reasoning/piece-identities.ts +159 -0
  203. package/src/chess-reasoning/poc-contracts.ts +1097 -0
  204. package/src/chess-reasoning/poc-hypotheses.ts +759 -0
  205. package/src/chess-reasoning/poc-move-witnesses.ts +170 -0
  206. package/src/chess-reasoning/poc-observations.ts +611 -0
  207. package/src/chess-reasoning/poc-piece-refs.ts +62 -0
  208. package/src/chess-reasoning/poc-plan-model.ts +345 -0
  209. package/src/chess-reasoning/poc-semantic-text.ts +10 -0
  210. package/src/chess-reasoning/poc-tools.ts +978 -0
  211. package/src/chess-reasoning/portable-answer-runtime.ts +684 -0
  212. package/src/chess-reasoning/position-analysis-contracts.ts +88 -0
  213. package/src/chess-reasoning/position-analysis-scope.ts +20 -0
  214. package/src/chess-reasoning/preparation-consolidation.ts +91 -0
  215. package/src/chess-reasoning/production-observation-provider.ts +41 -0
  216. package/src/chess-reasoning/question-routing.ts +332 -0
  217. package/src/chess-reasoning/recorded-main-line.ts +55 -0
  218. package/src/chess-reasoning/recorded-position-tools.ts +78 -0
  219. package/src/chess-reasoning/recorded-reference-context.ts +44 -0
  220. package/src/chess-reasoning/recorded-selected-classification.ts +54 -0
  221. package/src/chess-reasoning/reference-limits.ts +2 -0
  222. package/src/chess-reasoning/research-exports.ts +197 -0
  223. package/src/chess-reasoning/saved-score-presentation.ts +6 -0
  224. package/src/chess-reasoning/score-comparison-notices.ts +115 -0
  225. package/src/chess-reasoning/server-simple-policy.ts +75 -0
  226. package/src/chess-reasoning/simple-agent-config.ts +58 -0
  227. package/src/chess-reasoning/simple-board-analysis.ts +57 -0
  228. package/src/chess-reasoning/simple-compact-evidence.ts +79 -0
  229. package/src/chess-reasoning/simple-completion.ts +37 -0
  230. package/src/chess-reasoning/simple-engine-presentation.ts +95 -0
  231. package/src/chess-reasoning/simple-evidence-preparation.ts +32 -0
  232. package/src/chess-reasoning/simple-evidence-view.ts +69 -0
  233. package/src/chess-reasoning/simple-explanation-coverage.ts +118 -0
  234. package/src/chess-reasoning/simple-fact-delivery.ts +52 -0
  235. package/src/chess-reasoning/simple-fallback-premise.ts +65 -0
  236. package/src/chess-reasoning/simple-prose-references.ts +528 -0
  237. package/src/chess-reasoning/simple-runtime-version.ts +2 -0
  238. package/src/chess-reasoning/stable.ts +18 -0
  239. package/src/chess-reasoning/tool-input-loop-guard.ts +38 -0
  240. package/src/chess-reasoning/types.ts +195 -0
  241. package/src/chess-reasoning/visible-support-lines.ts +44 -0
  242. package/src/chess-reasoning/wikibooks-index.ts +56 -0
  243. package/src/chess-reasoning/wikibooks-tool-context.ts +91 -0
  244. package/src/player-perspective.ts +41 -0
  245. package/src/shared-exports.ts +23 -0
  246. package/test/answer-pipeline.test.mjs +375 -0
  247. package/test/owner/chess-reasoning-compact-search-preparation.test.mjs +111 -0
  248. package/test/owner/mechanism-encoding.test.mjs +78 -0
  249. package/test/owner/move-mechanism-facts.test.mjs +105 -0
  250. package/test/owner/pipeline-host-preparation.test.mjs +115 -0
  251. package/test/owner/position-preparation.test.mjs +22 -0
  252. package/test/owner/preparation-consolidation.test.mjs +74 -0
  253. package/test/owner/prose-reference-admission.test.mjs +26 -0
  254. package/test/owner/score-comparison-notices.test.mjs +116 -0
  255. package/test/owner/simple-engine-presentation.test.mjs +50 -0
  256. package/test/owner/tool-input-loop-guard.test.mjs +25 -0
  257. package/test-types.mts +23 -0
  258. package/tsconfig.json +22 -0
@@ -0,0 +1,396 @@
1
+ // test/owner/move-mechanism-facts.test.mjs
2
+ import assert from "node:assert/strict";
3
+ import test from "node:test";
4
+ import { Chess as Chess3 } from "chess.js";
5
+
6
+ // src/chess-reasoning/move-mechanism-facts.ts
7
+ import { Chess as Chess2, SQUARES as SQUARES2 } from "chess.js";
8
+
9
+ // src/chess-reasoning/piece-identities.ts
10
+ import { Chess, SQUARES } from "chess.js";
11
+
12
+ // src/chess-reasoning/types.ts
13
+ var CHESS_REASONING_SCHEMA_VERSION = "0.2.0";
14
+
15
+ // src/chess-reasoning/piece-identities.ts
16
+ var PIECE_NAMES = {
17
+ p: "pawn",
18
+ n: "knight",
19
+ b: "bishop",
20
+ r: "rook",
21
+ q: "queen",
22
+ k: "king"
23
+ };
24
+ var COLOR_NAMES = { w: "white", b: "black" };
25
+ function pieceId(color, type, square, ordinal = 0) {
26
+ return `${COLOR_NAMES[color]}-${PIECE_NAMES[type]}:${square}:${ordinal}`;
27
+ }
28
+ function promotionId(color, type, square, ordinal) {
29
+ return `${COLOR_NAMES[color]}-${PIECE_NAMES[type]}:promotion:${square}:${ordinal}`;
30
+ }
31
+ function clonePieces(pieces) {
32
+ return Object.fromEntries(Object.entries(pieces).map(([id, piece]) => [id, { ...piece }]));
33
+ }
34
+ function createIdentityState(fen = new Chess().fen(), branchId = "main", ply = 0) {
35
+ const chess = new Chess(fen);
36
+ const pieces = {};
37
+ const squares = {};
38
+ const seen = /* @__PURE__ */ new Map();
39
+ for (const square of SQUARES) {
40
+ const piece = chess.get(square);
41
+ if (!piece) continue;
42
+ const key = `${piece.color}:${piece.type}:${square}`;
43
+ const ordinal = seen.get(key) || 0;
44
+ seen.set(key, ordinal + 1);
45
+ const id = pieceId(piece.color, piece.type, square, ordinal);
46
+ pieces[id] = {
47
+ id,
48
+ color: piece.color,
49
+ type: piece.type,
50
+ originSquare: square,
51
+ createdPly: ply,
52
+ promotedFrom: null,
53
+ capturedAtPly: null,
54
+ retiredAtPly: null
55
+ };
56
+ squares[square] = id;
57
+ }
58
+ return {
59
+ schemaVersion: CHESS_REASONING_SCHEMA_VERSION,
60
+ branchId,
61
+ fen: chess.fen(),
62
+ ply,
63
+ pieces,
64
+ squares,
65
+ promotionSequence: 0
66
+ };
67
+ }
68
+ function applyIdentityMove(state, moveInput) {
69
+ const chess = new Chess(state.fen);
70
+ const beforeFen = chess.fen();
71
+ const move = chess.move(moveInput);
72
+ const nextPly = state.ply + 1;
73
+ const pieces = clonePieces(state.pieces);
74
+ const squares = { ...state.squares };
75
+ const mover = squares[move.from];
76
+ if (!mover) throw new Error(`No stable piece identity at ${move.from}`);
77
+ let captured = null;
78
+ const capturedSquare = move.isEnPassant() ? `${move.to[0]}${move.from[1]}` : move.to;
79
+ if (move.isCapture() || move.isEnPassant()) {
80
+ captured = squares[capturedSquare] || null;
81
+ if (!captured) throw new Error(`No captured piece identity at ${capturedSquare}`);
82
+ pieces[captured] = { ...pieces[captured], capturedAtPly: nextPly };
83
+ delete squares[capturedSquare];
84
+ }
85
+ delete squares[move.from];
86
+ let rookMoved = null;
87
+ if (move.isKingsideCastle() || move.isQueensideCastle()) {
88
+ const rank = move.from[1];
89
+ const rookFrom = `${move.isKingsideCastle() ? "h" : "a"}${rank}`;
90
+ const rookTo = `${move.isKingsideCastle() ? "f" : "d"}${rank}`;
91
+ rookMoved = squares[rookFrom] || null;
92
+ if (!rookMoved) throw new Error(`No stable rook identity at ${rookFrom}`);
93
+ delete squares[rookFrom];
94
+ squares[rookTo] = rookMoved;
95
+ }
96
+ let promoted = null;
97
+ let promotionSequence = state.promotionSequence;
98
+ if (move.isPromotion()) {
99
+ promotionSequence += 1;
100
+ pieces[mover] = { ...pieces[mover], retiredAtPly: nextPly };
101
+ promoted = promotionId(move.color, move.promotion, move.to, promotionSequence);
102
+ pieces[promoted] = {
103
+ id: promoted,
104
+ color: move.color,
105
+ type: move.promotion,
106
+ originSquare: move.to,
107
+ createdPly: nextPly,
108
+ promotedFrom: mover,
109
+ capturedAtPly: null,
110
+ retiredAtPly: null
111
+ };
112
+ squares[move.to] = promoted;
113
+ } else {
114
+ squares[move.to] = mover;
115
+ }
116
+ const nextState = {
117
+ schemaVersion: CHESS_REASONING_SCHEMA_VERSION,
118
+ branchId: state.branchId,
119
+ fen: chess.fen(),
120
+ ply: nextPly,
121
+ pieces,
122
+ squares,
123
+ promotionSequence
124
+ };
125
+ return {
126
+ state: nextState,
127
+ delta: {
128
+ branchId: state.branchId,
129
+ ply: nextPly,
130
+ uci: `${move.from}${move.to}${move.promotion || ""}`,
131
+ san: move.san,
132
+ mover,
133
+ captured,
134
+ promoted,
135
+ rookMoved,
136
+ beforeFen,
137
+ afterFen: chess.fen()
138
+ }
139
+ };
140
+ }
141
+
142
+ // src/chess-reasoning/move-mechanism-facts.ts
143
+ var DIRECTIONS = [
144
+ ["N", 0, 1],
145
+ ["NE", 1, 1],
146
+ ["E", 1, 0],
147
+ ["SE", 1, -1],
148
+ ["S", 0, -1],
149
+ ["SW", -1, -1],
150
+ ["W", -1, 0],
151
+ ["NW", -1, 1]
152
+ ];
153
+ function enemy(color) {
154
+ return color === "w" ? "b" : "w";
155
+ }
156
+ function attackers(board, square, color) {
157
+ return board.attackers(square, color).sort((a, b) => SQUARES2.indexOf(a) - SQUARES2.indexOf(b));
158
+ }
159
+ function ray(board, origin, fileStep, rankStep) {
160
+ const squares = [];
161
+ const file = origin.charCodeAt(0) - 97;
162
+ const rank = Number(origin[1]) - 1;
163
+ for (let distance = 1; distance < 8; distance += 1) {
164
+ const f = file + fileStep * distance;
165
+ const r = rank + rankStep * distance;
166
+ if (f < 0 || f > 7 || r < 0 || r > 7) break;
167
+ const square = `${String.fromCharCode(97 + f)}${r + 1}`;
168
+ squares.push(square);
169
+ if (board.get(square)) break;
170
+ }
171
+ return squares;
172
+ }
173
+ function sliderDirections(type) {
174
+ if (type === "q") return DIRECTIONS;
175
+ return DIRECTIONS.filter(([, dx, dy]) => type === "r" ? !dx || !dy : !!dx && !!dy);
176
+ }
177
+ function moveMechanismFacts(before, after, from, to, beforeIds, afterIds) {
178
+ const moverBefore = before.get(from);
179
+ const moverAfter = after.get(to);
180
+ if (!moverBefore || !moverAfter || moverBefore.color !== moverAfter.color) {
181
+ throw new Error(`Move mechanism has no corresponding mover at ${from}-${to}`);
182
+ }
183
+ const beforeState = beforeIds || createIdentityState(before.fen());
184
+ if (beforeState.fen !== before.fen() || afterIds && afterIds.fen !== after.fen()) {
185
+ throw new Error("Move mechanism identity position does not match the board");
186
+ }
187
+ const actualMove = before.moves({ verbose: true }).find((move) => {
188
+ if (move.from !== from || move.to !== to) return false;
189
+ const replay = new Chess2(before.fen());
190
+ replay.move({ from, to, promotion: move.promotion });
191
+ return replay.fen() === after.fen();
192
+ });
193
+ if (!actualMove) throw new Error(`Move mechanism after-position is not a legal ${from}-${to} result`);
194
+ const afterState = afterIds || applyIdentityMove(beforeState, {
195
+ from,
196
+ to,
197
+ promotion: actualMove.promotion
198
+ }).state;
199
+ const pieceId2 = beforeState.squares[from];
200
+ const pieceIdAfter = afterState.squares[to];
201
+ if (!pieceId2 || !pieceIdAfter || (actualMove.promotion ? afterState.pieces[pieceIdAfter]?.promotedFrom !== pieceId2 : pieceIdAfter !== pieceId2)) {
202
+ throw new Error("Move mechanism mover identities do not match");
203
+ }
204
+ const attackedUnmovedFriendlyPieces = [];
205
+ const stationarySliderRays = [];
206
+ const color = moverBefore.color;
207
+ for (const square of SQUARES2) {
208
+ const piece = before.get(square);
209
+ const samePiece = after.get(square);
210
+ const id = beforeState.squares[square];
211
+ if (!piece || !samePiece || !id || afterState.squares[square] !== id || piece.color !== samePiece.color || piece.type !== samePiece.type) continue;
212
+ if (piece.color === color) {
213
+ const beforeAttackers = attackers(before, square, enemy(color));
214
+ const afterAttackers = attackers(after, square, enemy(color));
215
+ if (beforeAttackers.length || afterAttackers.length) {
216
+ attackedUnmovedFriendlyPieces.push({
217
+ pieceId: id,
218
+ square,
219
+ pieceType: piece.type,
220
+ attackersBefore: beforeAttackers,
221
+ attackersAfter: afterAttackers
222
+ });
223
+ }
224
+ }
225
+ if (piece.type !== "r" && piece.type !== "b" && piece.type !== "q") continue;
226
+ for (const [direction, dx, dy] of sliderDirections(piece.type)) {
227
+ const beforeSquares = ray(before, square, dx, dy);
228
+ const afterSquares = ray(after, square, dx, dy);
229
+ const beforeSet = new Set(beforeSquares);
230
+ const afterSet = new Set(afterSquares);
231
+ const gainedSquares = afterSquares.filter((target) => !beforeSet.has(target));
232
+ const lostSquares = beforeSquares.filter((target) => !afterSet.has(target));
233
+ if (gainedSquares.length || lostSquares.length) {
234
+ stationarySliderRays.push({
235
+ pieceId: id,
236
+ fromSquare: square,
237
+ color: piece.color,
238
+ pieceType: piece.type,
239
+ direction,
240
+ beforeSquares,
241
+ afterSquares,
242
+ gainedSquares,
243
+ lostSquares
244
+ });
245
+ }
246
+ }
247
+ }
248
+ const controlDelta = [];
249
+ for (const square of SQUARES2) {
250
+ for (const attackingColor of ["w", "b"]) {
251
+ const attackersBefore = attackers(before, square, attackingColor);
252
+ const attackersAfter = attackers(after, square, attackingColor);
253
+ if (attackersBefore.length !== attackersAfter.length || attackersBefore.some((origin, index) => origin !== attackersAfter[index])) {
254
+ controlDelta.push({ square, color: attackingColor, attackersBefore, attackersAfter });
255
+ }
256
+ }
257
+ }
258
+ return {
259
+ mover: {
260
+ pieceId: pieceId2,
261
+ pieceIdAfter,
262
+ fromSquare: from,
263
+ toSquare: to,
264
+ color,
265
+ pieceTypeBefore: moverBefore.type,
266
+ pieceTypeAfter: moverAfter.type,
267
+ attackersBefore: attackers(before, from, enemy(color)),
268
+ attackersAfter: attackers(after, to, enemy(color))
269
+ },
270
+ attackedUnmovedFriendlyPieces,
271
+ stationarySliderRays,
272
+ controlDelta,
273
+ checksRun: [
274
+ "mover-geometric-attackers-before-after",
275
+ "unmoved-friendly-piece-geometric-attackers-before-after",
276
+ "stationary-slider-visible-rays-before-after",
277
+ "all-square-both-color-geometric-control-delta"
278
+ ],
279
+ scope: "Complete board-local geometric measurements at this move. Attackers include pinned pieces; visible slider rays include the first occupied square. Empty lists mean measured none or no change. No claim of legal captures, safe retreat, exchange profit, forced purpose, or strategic importance."
280
+ };
281
+ }
282
+
283
+ // test/owner/move-mechanism-facts.test.mjs
284
+ function facts(fen, uci, suppliedIdentities = true) {
285
+ const before = new Chess3(fen);
286
+ const beforeIds = createIdentityState(fen);
287
+ const { state: afterIds } = applyIdentityMove(beforeIds, {
288
+ from: uci.slice(0, 2),
289
+ to: uci.slice(2, 4),
290
+ promotion: uci.slice(4) || void 0
291
+ });
292
+ const after = new Chess3(afterIds.fen);
293
+ return moveMechanismFacts(
294
+ before,
295
+ after,
296
+ uci.slice(0, 2),
297
+ uci.slice(2, 4),
298
+ ...suppliedIdentities ? [beforeIds, afterIds] : []
299
+ );
300
+ }
301
+ test("KE-REASON-016/021: Rb2+ mover is unattacked while the unmoved rook on d1 is attacked from a4", () => {
302
+ const observed = facts("1k1r3r/2p5/p2p1p2/2P2B2/b3PB1p/5P2/3R2PP/2KR4 w - - 1 25", "d2b2");
303
+ assert.equal(observed.mover.pieceId, "white-rook:d2:0");
304
+ assert.deepEqual(observed.mover.attackersBefore, []);
305
+ assert.deepEqual(observed.mover.attackersAfter, []);
306
+ assert.deepEqual(observed.attackedUnmovedFriendlyPieces.find((piece) => piece.square === "d1"), {
307
+ pieceId: "white-rook:d1:0",
308
+ square: "d1",
309
+ pieceType: "r",
310
+ attackersBefore: ["a4"],
311
+ attackersAfter: ["a4"]
312
+ });
313
+ assert.ok(observed.checksRun.includes("unmoved-friendly-piece-geometric-attackers-before-after"));
314
+ assert.match(observed.scope, /geometric measurements/i);
315
+ });
316
+ test("KE-REASON-016/021: after 7...b6, e5 frees the queen d4 horizontal ray through f4-h4, not a8", () => {
317
+ const observed = facts("r1bqkb1r/p1pp1pp1/1p3n1p/8/2BQP3/2N5/PPP2PPP/R1B1K2R w KQkq - 0 8", "e4e5");
318
+ const east = observed.stationarySliderRays.find((item) => item.fromSquare === "d4" && item.direction === "E");
319
+ assert.deepEqual(east?.beforeSquares, ["e4"]);
320
+ assert.deepEqual(east?.afterSquares, ["e4", "f4", "g4", "h4"]);
321
+ assert.deepEqual(east?.gainedSquares, ["f4", "g4", "h4"]);
322
+ assert.ok(!observed.stationarySliderRays.some((item) => item.fromSquare === "d4" && item.gainedSquares.includes("a8")));
323
+ assert.deepEqual(observed.controlDelta.find((item) => item.square === "g4" && item.color === "w")?.attackersAfter, ["d4"]);
324
+ });
325
+ test("KE-REASON-016/021: control loss occurs at the actual knight move, not the prior pawn push", () => {
326
+ const rootFen = "r1bqkb1r/p1pp1pp1/1p3n1p/8/2BQP3/2N5/PPP2PPP/R1B1K2R w KQkq - 0 8";
327
+ const pawnPush = facts(rootFen, "e4e5");
328
+ assert.equal(pawnPush.controlDelta.find((item) => item.square === "d5" && item.color === "b"), void 0);
329
+ const afterPawnPush = new Chess3(rootFen);
330
+ afterPawnPush.move({ from: "e4", to: "e5" });
331
+ const knightMove = facts(afterPawnPush.fen(), "f6h7");
332
+ assert.deepEqual(knightMove.controlDelta.find((item) => item.square === "d5" && item.color === "b"), {
333
+ square: "d5",
334
+ color: "b",
335
+ attackersBefore: ["f6"],
336
+ attackersAfter: []
337
+ });
338
+ assert.deepEqual(knightMove.mover.attackersBefore, ["e5"]);
339
+ });
340
+ test("KE-REASON-016/021: moving slider is not reported as stationary opening, even along same file", () => {
341
+ const rootFen = "k1B5/1R6/p2B1p2/8/4P2p/5P2/6PP/2K2b2 w - - 7 34";
342
+ const observed = facts(rootFen, "b7b2");
343
+ assert.ok(!observed.stationarySliderRays.some((item) => item.pieceId === observed.mover.pieceId));
344
+ assert.ok(!observed.stationarySliderRays.some((item) => item.fromSquare === "b7" || item.fromSquare === "b2"));
345
+ const afterRook = new Chess3(rootFen);
346
+ afterRook.move({ from: "b7", to: "b2" });
347
+ const response = facts(afterRook.fen(), "f1b5");
348
+ const blocked = response.stationarySliderRays.find((item) => item.fromSquare === "b2" && item.direction === "N");
349
+ assert.deepEqual(blocked?.beforeSquares, ["b3", "b4", "b5", "b6", "b7", "b8"]);
350
+ assert.deepEqual(blocked?.afterSquares, ["b3", "b4", "b5"]);
351
+ assert.deepEqual(blocked?.lostSquares, ["b6", "b7", "b8"]);
352
+ });
353
+ test("KE-REASON-016/021: Bf7+, queen promotion and bishop underpromotion have exact mover identities and controls", () => {
354
+ const bishop = facts("8/4P3/1R2B3/6Bk/7P/8/8/2K5 w - - 3 50", "e6f7");
355
+ assert.equal(bishop.mover.pieceId, bishop.mover.pieceIdAfter);
356
+ assert.equal(bishop.mover.pieceTypeBefore, "b");
357
+ assert.deepEqual(bishop.controlDelta.find((item) => item.square === "e8" && item.color === "w")?.attackersAfter, ["f7"]);
358
+ assert.deepEqual(bishop.controlDelta.find((item) => item.square === "g4" && item.color === "w"), {
359
+ square: "g4",
360
+ color: "w",
361
+ attackersBefore: ["e6"],
362
+ attackersAfter: []
363
+ });
364
+ const mateFen = "8/4P3/1R2B3/6Bk/7P/8/8/2K5 w - - 3 50";
365
+ for (const uci of ["e7e8q", "e7e8b"]) {
366
+ const promotionMate = new Chess3(mateFen);
367
+ promotionMate.move({ from: "e7", to: "e8", promotion: uci.slice(4) });
368
+ assert.equal(promotionMate.isCheckmate(), true, `${uci} is a legal mate at the observed board`);
369
+ assert.equal(facts(mateFen, uci).mover.pieceTypeAfter, uci.slice(4));
370
+ }
371
+ const promotionFen = "8/4P3/1R2B3/6B1/7P/5k2/8/2K5 w - - 7 52";
372
+ const queen = facts(promotionFen, "e7e8q");
373
+ const underpromotion = facts(promotionFen, "e7e8b", false);
374
+ assert.equal(queen.mover.pieceTypeAfter, "q");
375
+ assert.equal(underpromotion.mover.pieceTypeAfter, "b");
376
+ assert.notEqual(queen.mover.pieceId, queen.mover.pieceIdAfter);
377
+ assert.match(queen.mover.pieceIdAfter, /promotion:e8/);
378
+ assert.ok(queen.controlDelta.some((entry) => entry.square === "e7" && entry.color === "w"));
379
+ assert.ok(!underpromotion.controlDelta.some((entry) => entry.square === "e7" && entry.color === "w"));
380
+ });
381
+ test("KE-REASON-016/021: en passant removes the off-target blocker and castling excludes the moved rook", () => {
382
+ const ep = facts("7k/8/8/3pP3/8/8/8/K2R4 w - d6 0 1", "e5d6");
383
+ assert.deepEqual(ep.controlDelta.find((item) => item.square === "e4" && item.color === "b"), {
384
+ square: "e4",
385
+ color: "b",
386
+ attackersBefore: ["d5"],
387
+ attackersAfter: []
388
+ });
389
+ assert.deepEqual(
390
+ ep.stationarySliderRays.find((item) => item.fromSquare === "d1" && item.direction === "N")?.afterSquares,
391
+ ["d2", "d3", "d4", "d5", "d6"]
392
+ );
393
+ const castle = facts("4k3/8/8/8/8/8/8/4K2R w K - 0 1", "e1g1");
394
+ assert.ok(!castle.stationarySliderRays.some((item) => item.pieceId === "white-rook:h1:0"));
395
+ assert.equal(castle.mover.toSquare, "g1");
396
+ });