@aephia/atlas-kit 0.6.0-next.13 → 0.6.0-next.130

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 (752) hide show
  1. package/README.md +3 -1
  2. package/dist/cargo/actions/deposit.d.ts +2 -2
  3. package/dist/cargo/actions/deposit.d.ts.map +1 -1
  4. package/dist/cargo/actions/deposit.js +28 -117
  5. package/dist/cargo/actions/deposit.js.map +1 -1
  6. package/dist/cargo/actions/index.d.ts +1 -1
  7. package/dist/cargo/actions/index.d.ts.map +1 -1
  8. package/dist/cargo/actions/index.js +28 -84
  9. package/dist/cargo/actions/index.js.map +1 -1
  10. package/dist/cargo/actions/wallet-context.d.ts +52 -3
  11. package/dist/cargo/actions/wallet-context.d.ts.map +1 -1
  12. package/dist/cargo/actions/wallet-context.js +118 -7
  13. package/dist/cargo/actions/wallet-context.js.map +1 -1
  14. package/dist/cargo/actions/wallet-token.d.ts +1 -6
  15. package/dist/cargo/actions/wallet-token.d.ts.map +1 -1
  16. package/dist/cargo/actions/wallet-token.js +0 -53
  17. package/dist/cargo/actions/wallet-token.js.map +1 -1
  18. package/dist/cargo/actions/withdrawal.d.ts +1 -1
  19. package/dist/cargo/actions/withdrawal.d.ts.map +1 -1
  20. package/dist/cargo/actions/withdrawal.js +39 -164
  21. package/dist/cargo/actions/withdrawal.js.map +1 -1
  22. package/dist/cargo/index.d.ts.map +1 -1
  23. package/dist/cargo/index.js +2 -10
  24. package/dist/cargo/index.js.map +1 -1
  25. package/dist/claim-stakes/actions/index.d.ts +57 -8
  26. package/dist/claim-stakes/actions/index.d.ts.map +1 -1
  27. package/dist/claim-stakes/actions/index.js +542 -675
  28. package/dist/claim-stakes/actions/index.js.map +1 -1
  29. package/dist/claim-stakes/index.d.ts +14 -0
  30. package/dist/claim-stakes/index.d.ts.map +1 -1
  31. package/dist/claim-stakes/index.js +25 -12
  32. package/dist/claim-stakes/index.js.map +1 -1
  33. package/dist/client/context.d.ts.map +1 -1
  34. package/dist/client/context.js +1 -1
  35. package/dist/client/context.js.map +1 -1
  36. package/dist/client/contracts.d.ts +16 -6
  37. package/dist/client/contracts.d.ts.map +1 -1
  38. package/dist/client/errors.d.ts +2 -2
  39. package/dist/client/errors.d.ts.map +1 -1
  40. package/dist/client/errors.js.map +1 -1
  41. package/dist/client/index.d.ts +35 -0
  42. package/dist/client/index.d.ts.map +1 -1
  43. package/dist/client/index.js +35 -0
  44. package/dist/client/index.js.map +1 -1
  45. package/dist/client/rpc-subscription-provider.d.ts +35 -0
  46. package/dist/client/rpc-subscription-provider.d.ts.map +1 -0
  47. package/dist/client/rpc-subscription-provider.js +190 -0
  48. package/dist/client/rpc-subscription-provider.js.map +1 -0
  49. package/dist/combat/actions/index.d.ts +15 -9
  50. package/dist/combat/actions/index.d.ts.map +1 -1
  51. package/dist/combat/actions/index.js +245 -274
  52. package/dist/combat/actions/index.js.map +1 -1
  53. package/dist/combat/index.d.ts +20 -5
  54. package/dist/combat/index.d.ts.map +1 -1
  55. package/dist/combat/index.js +12 -17
  56. package/dist/combat/index.js.map +1 -1
  57. package/dist/crafting/actions/index.d.ts +16 -5
  58. package/dist/crafting/actions/index.d.ts.map +1 -1
  59. package/dist/crafting/actions/index.js +204 -318
  60. package/dist/crafting/actions/index.js.map +1 -1
  61. package/dist/crafting/index.d.ts +33 -3
  62. package/dist/crafting/index.d.ts.map +1 -1
  63. package/dist/crafting/index.js +49 -28
  64. package/dist/crafting/index.js.map +1 -1
  65. package/dist/factions/actions/index.d.ts +2 -1
  66. package/dist/factions/actions/index.d.ts.map +1 -1
  67. package/dist/factions/actions/index.js +6 -4
  68. package/dist/factions/actions/index.js.map +1 -1
  69. package/dist/factions/index.d.ts +6 -1
  70. package/dist/factions/index.d.ts.map +1 -1
  71. package/dist/factions/index.js +16 -18
  72. package/dist/factions/index.js.map +1 -1
  73. package/dist/factions/profile-faction.d.ts +20 -8
  74. package/dist/factions/profile-faction.d.ts.map +1 -1
  75. package/dist/factions/profile-faction.js +20 -8
  76. package/dist/factions/profile-faction.js.map +1 -1
  77. package/dist/fleets/actions/add-ship.d.ts +35 -0
  78. package/dist/fleets/actions/add-ship.d.ts.map +1 -0
  79. package/dist/fleets/actions/add-ship.js +120 -0
  80. package/dist/fleets/actions/add-ship.js.map +1 -0
  81. package/dist/fleets/actions/create-fleet.d.ts +36 -0
  82. package/dist/fleets/actions/create-fleet.d.ts.map +1 -0
  83. package/dist/fleets/actions/create-fleet.js +108 -0
  84. package/dist/fleets/actions/create-fleet.js.map +1 -0
  85. package/dist/fleets/actions/crew-transfer.d.ts +41 -0
  86. package/dist/fleets/actions/crew-transfer.d.ts.map +1 -0
  87. package/dist/fleets/actions/crew-transfer.js +94 -0
  88. package/dist/fleets/actions/crew-transfer.js.map +1 -0
  89. package/dist/fleets/actions/dock-bounds.d.ts +38 -0
  90. package/dist/fleets/actions/dock-bounds.d.ts.map +1 -0
  91. package/dist/fleets/actions/dock-bounds.js +46 -0
  92. package/dist/fleets/actions/dock-bounds.js.map +1 -0
  93. package/dist/fleets/actions/index.d.ts +68 -26
  94. package/dist/fleets/actions/index.d.ts.map +1 -1
  95. package/dist/fleets/actions/index.js +395 -335
  96. package/dist/fleets/actions/index.js.map +1 -1
  97. package/dist/fleets/actions/respawn.d.ts +38 -0
  98. package/dist/fleets/actions/respawn.d.ts.map +1 -0
  99. package/dist/fleets/actions/respawn.js +93 -0
  100. package/dist/fleets/actions/respawn.js.map +1 -0
  101. package/dist/fleets/actions/ship-import.d.ts +1 -1
  102. package/dist/fleets/actions/ship-import.d.ts.map +1 -1
  103. package/dist/fleets/actions/ship-import.js +67 -91
  104. package/dist/fleets/actions/ship-import.js.map +1 -1
  105. package/dist/fleets/cooldown.d.ts +44 -0
  106. package/dist/fleets/cooldown.d.ts.map +1 -0
  107. package/dist/fleets/cooldown.js +43 -0
  108. package/dist/fleets/cooldown.js.map +1 -0
  109. package/dist/fleets/coordinate-warp-estimate.d.ts +70 -0
  110. package/dist/fleets/coordinate-warp-estimate.d.ts.map +1 -0
  111. package/dist/fleets/coordinate-warp-estimate.js +46 -0
  112. package/dist/fleets/coordinate-warp-estimate.js.map +1 -0
  113. package/dist/fleets/index.d.ts +35 -0
  114. package/dist/fleets/index.d.ts.map +1 -1
  115. package/dist/fleets/index.js +54 -12
  116. package/dist/fleets/index.js.map +1 -1
  117. package/dist/fleets/movement-arithmetic.d.ts +21 -0
  118. package/dist/fleets/movement-arithmetic.d.ts.map +1 -0
  119. package/dist/fleets/movement-arithmetic.js +41 -0
  120. package/dist/fleets/movement-arithmetic.js.map +1 -0
  121. package/dist/fleets/subwarp-estimate.d.ts +70 -0
  122. package/dist/fleets/subwarp-estimate.d.ts.map +1 -0
  123. package/dist/fleets/subwarp-estimate.js +38 -0
  124. package/dist/fleets/subwarp-estimate.js.map +1 -0
  125. package/dist/fleets/travel-profile.d.ts +102 -0
  126. package/dist/fleets/travel-profile.d.ts.map +1 -0
  127. package/dist/fleets/travel-profile.js +120 -0
  128. package/dist/fleets/travel-profile.js.map +1 -0
  129. package/dist/fleets/warp-lane-estimate.d.ts +59 -0
  130. package/dist/fleets/warp-lane-estimate.d.ts.map +1 -0
  131. package/dist/fleets/warp-lane-estimate.js +36 -0
  132. package/dist/fleets/warp-lane-estimate.js.map +1 -0
  133. package/dist/fleets/warp-lane-policy.d.ts +68 -0
  134. package/dist/fleets/warp-lane-policy.d.ts.map +1 -0
  135. package/dist/fleets/warp-lane-policy.js +91 -0
  136. package/dist/fleets/warp-lane-policy.js.map +1 -0
  137. package/dist/fleets/warp-lane-toll.d.ts +55 -0
  138. package/dist/fleets/warp-lane-toll.d.ts.map +1 -0
  139. package/dist/fleets/warp-lane-toll.js +79 -0
  140. package/dist/fleets/warp-lane-toll.js.map +1 -0
  141. package/dist/identity/actions/create-profile.d.ts +1 -1
  142. package/dist/identity/actions/create-profile.d.ts.map +1 -1
  143. package/dist/identity/actions/create-profile.js +2 -2
  144. package/dist/identity/actions/create-profile.js.map +1 -1
  145. package/dist/identity/actions/index.d.ts +5 -4
  146. package/dist/identity/actions/index.d.ts.map +1 -1
  147. package/dist/identity/actions/index.js +52 -29
  148. package/dist/identity/actions/index.js.map +1 -1
  149. package/dist/identity/actions/research-unlock.d.ts +52 -0
  150. package/dist/identity/actions/research-unlock.d.ts.map +1 -0
  151. package/dist/identity/actions/research-unlock.js +155 -0
  152. package/dist/identity/actions/research-unlock.js.map +1 -0
  153. package/dist/identity/index.d.ts +5 -2
  154. package/dist/identity/index.d.ts.map +1 -1
  155. package/dist/identity/index.js +6 -12
  156. package/dist/identity/index.js.map +1 -1
  157. package/dist/identity/research-state.d.ts +60 -0
  158. package/dist/identity/research-state.d.ts.map +1 -0
  159. package/dist/identity/research-state.js +68 -0
  160. package/dist/identity/research-state.js.map +1 -0
  161. package/dist/index.d.ts +24 -0
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +20 -3
  164. package/dist/index.js.map +1 -1
  165. package/dist/internal/c4/account-decoding.d.ts +49 -0
  166. package/dist/internal/c4/account-decoding.d.ts.map +1 -0
  167. package/dist/internal/c4/account-decoding.js +74 -0
  168. package/dist/internal/c4/account-decoding.js.map +1 -0
  169. package/dist/internal/c4/cargo-bytes-config.d.ts +7 -0
  170. package/dist/internal/c4/cargo-bytes-config.d.ts.map +1 -0
  171. package/dist/internal/c4/cargo-bytes-config.js +19 -0
  172. package/dist/internal/c4/cargo-bytes-config.js.map +1 -0
  173. package/dist/internal/c4/cargo-pod.d.ts +15 -0
  174. package/dist/internal/c4/cargo-pod.d.ts.map +1 -0
  175. package/dist/internal/c4/cargo-pod.js +13 -0
  176. package/dist/internal/c4/cargo-pod.js.map +1 -0
  177. package/dist/internal/c4/character-modifier.d.ts +1 -1
  178. package/dist/internal/c4/character-modifier.d.ts.map +1 -1
  179. package/dist/internal/c4/character-registration.d.ts +2 -0
  180. package/dist/internal/c4/character-registration.d.ts.map +1 -1
  181. package/dist/internal/c4/character-registration.js +2 -0
  182. package/dist/internal/c4/character-registration.js.map +1 -1
  183. package/dist/internal/c4/character.d.ts +157 -0
  184. package/dist/internal/c4/character.d.ts.map +1 -0
  185. package/dist/internal/c4/character.js +64 -0
  186. package/dist/internal/c4/character.js.map +1 -0
  187. package/dist/internal/c4/claim-stake-building-definitions.d.ts.map +1 -1
  188. package/dist/internal/c4/claim-stake-building-definitions.js +2 -1
  189. package/dist/internal/c4/claim-stake-building-definitions.js.map +1 -1
  190. package/dist/internal/c4/claim-stake-configuration.d.ts.map +1 -1
  191. package/dist/internal/c4/claim-stake-configuration.js +5 -36
  192. package/dist/internal/c4/claim-stake-configuration.js.map +1 -1
  193. package/dist/internal/c4/claim-stake-definitions.d.ts +4 -4
  194. package/dist/internal/c4/claim-stake-definitions.d.ts.map +1 -1
  195. package/dist/internal/c4/claim-stake-definitions.js +2 -1
  196. package/dist/internal/c4/claim-stake-definitions.js.map +1 -1
  197. package/dist/internal/c4/claim-stake-plots.d.ts +17 -0
  198. package/dist/internal/c4/claim-stake-plots.d.ts.map +1 -0
  199. package/dist/internal/c4/claim-stake-plots.js +29 -0
  200. package/dist/internal/c4/claim-stake-plots.js.map +1 -0
  201. package/dist/internal/c4/claim-stake.d.ts +7 -27
  202. package/dist/internal/c4/claim-stake.d.ts.map +1 -1
  203. package/dist/internal/c4/claim-stake.js +38 -83
  204. package/dist/internal/c4/claim-stake.js.map +1 -1
  205. package/dist/internal/c4/combat.d.ts +1 -82
  206. package/dist/internal/c4/combat.d.ts.map +1 -1
  207. package/dist/internal/c4/combat.js +16 -213
  208. package/dist/internal/c4/combat.js.map +1 -1
  209. package/dist/internal/c4/crafting-process-codec.d.ts +13 -0
  210. package/dist/internal/c4/crafting-process-codec.d.ts.map +1 -0
  211. package/dist/internal/c4/crafting-process-codec.js +34 -0
  212. package/dist/internal/c4/crafting-process-codec.js.map +1 -0
  213. package/dist/internal/c4/crafting.d.ts +27 -33
  214. package/dist/internal/c4/crafting.d.ts.map +1 -1
  215. package/dist/internal/c4/crafting.js +76 -127
  216. package/dist/internal/c4/crafting.js.map +1 -1
  217. package/dist/internal/c4/currency-cache.d.ts +6 -0
  218. package/dist/internal/c4/currency-cache.d.ts.map +1 -0
  219. package/dist/internal/c4/currency-cache.js +27 -0
  220. package/dist/internal/c4/currency-cache.js.map +1 -0
  221. package/dist/internal/c4/discriminators.d.ts +85 -0
  222. package/dist/internal/c4/discriminators.d.ts.map +1 -0
  223. package/dist/internal/c4/discriminators.js +174 -0
  224. package/dist/internal/c4/discriminators.js.map +1 -0
  225. package/dist/internal/c4/docking-funding.d.ts +4 -0
  226. package/dist/internal/c4/docking-funding.d.ts.map +1 -0
  227. package/dist/internal/c4/docking-funding.js +11 -0
  228. package/dist/internal/c4/docking-funding.js.map +1 -0
  229. package/dist/internal/c4/faction-epoch-meter.d.ts +10 -0
  230. package/dist/internal/c4/faction-epoch-meter.d.ts.map +1 -0
  231. package/dist/internal/c4/faction-epoch-meter.js +26 -0
  232. package/dist/internal/c4/faction-epoch-meter.js.map +1 -0
  233. package/dist/internal/c4/faction-treasury.d.ts +23 -0
  234. package/dist/internal/c4/faction-treasury.d.ts.map +1 -0
  235. package/dist/internal/c4/faction-treasury.js +41 -0
  236. package/dist/internal/c4/faction-treasury.js.map +1 -0
  237. package/dist/internal/c4/faction.d.ts +21 -0
  238. package/dist/internal/c4/faction.d.ts.map +1 -0
  239. package/dist/internal/c4/faction.js +23 -0
  240. package/dist/internal/c4/faction.js.map +1 -0
  241. package/dist/internal/c4/factions.d.ts +15 -57
  242. package/dist/internal/c4/factions.d.ts.map +1 -1
  243. package/dist/internal/c4/factions.js +80 -223
  244. package/dist/internal/c4/factions.js.map +1 -1
  245. package/dist/internal/c4/fleet-arrival.d.ts +28 -0
  246. package/dist/internal/c4/fleet-arrival.d.ts.map +1 -0
  247. package/dist/internal/c4/fleet-arrival.js +42 -0
  248. package/dist/internal/c4/fleet-arrival.js.map +1 -0
  249. package/dist/internal/c4/fleet-creation.d.ts +7 -0
  250. package/dist/internal/c4/fleet-creation.d.ts.map +1 -0
  251. package/dist/internal/c4/fleet-creation.js +171 -0
  252. package/dist/internal/c4/fleet-creation.js.map +1 -0
  253. package/dist/internal/c4/fleet-crew-binding.d.ts +9 -0
  254. package/dist/internal/c4/fleet-crew-binding.d.ts.map +1 -1
  255. package/dist/internal/c4/fleet-crew-binding.js +31 -44
  256. package/dist/internal/c4/fleet-crew-binding.js.map +1 -1
  257. package/dist/internal/c4/fleet-respawn.d.ts +17 -0
  258. package/dist/internal/c4/fleet-respawn.d.ts.map +1 -0
  259. package/dist/internal/c4/fleet-respawn.js +62 -0
  260. package/dist/internal/c4/fleet-respawn.js.map +1 -0
  261. package/dist/internal/c4/fleet-ship-assembly.d.ts +11 -0
  262. package/dist/internal/c4/fleet-ship-assembly.d.ts.map +1 -0
  263. package/dist/internal/c4/fleet-ship-assembly.js +44 -0
  264. package/dist/internal/c4/fleet-ship-assembly.js.map +1 -0
  265. package/dist/internal/c4/fleet.d.ts +48 -19
  266. package/dist/internal/c4/fleet.d.ts.map +1 -1
  267. package/dist/internal/c4/fleet.js +50 -87
  268. package/dist/internal/c4/fleet.js.map +1 -1
  269. package/dist/internal/c4/game-definition-layout.d.ts +2 -0
  270. package/dist/internal/c4/game-definition-layout.d.ts.map +1 -1
  271. package/dist/internal/c4/game-definition-layout.js +4 -0
  272. package/dist/internal/c4/game-definition-layout.js.map +1 -1
  273. package/dist/internal/c4/game.d.ts +101 -30
  274. package/dist/internal/c4/game.d.ts.map +1 -1
  275. package/dist/internal/c4/game.js +203 -124
  276. package/dist/internal/c4/game.js.map +1 -1
  277. package/dist/internal/c4/identity.d.ts +3 -152
  278. package/dist/internal/c4/identity.d.ts.map +1 -1
  279. package/dist/internal/c4/identity.js +19 -126
  280. package/dist/internal/c4/identity.js.map +1 -1
  281. package/dist/internal/c4/local-market-order.d.ts +29 -0
  282. package/dist/internal/c4/local-market-order.d.ts.map +1 -0
  283. package/dist/internal/c4/local-market-order.js +46 -0
  284. package/dist/internal/c4/local-market-order.js.map +1 -0
  285. package/dist/internal/c4/loot-layout.js +1 -1
  286. package/dist/internal/c4/loot-layout.js.map +1 -1
  287. package/dist/internal/c4/loot.d.ts +89 -0
  288. package/dist/internal/c4/loot.d.ts.map +1 -0
  289. package/dist/internal/c4/loot.js +109 -0
  290. package/dist/internal/c4/loot.js.map +1 -0
  291. package/dist/internal/c4/loyalty.d.ts +0 -27
  292. package/dist/internal/c4/loyalty.d.ts.map +1 -1
  293. package/dist/internal/c4/loyalty.js +32 -160
  294. package/dist/internal/c4/loyalty.js.map +1 -1
  295. package/dist/internal/c4/market.d.ts +2 -38
  296. package/dist/internal/c4/market.d.ts.map +1 -1
  297. package/dist/internal/c4/market.js +26 -120
  298. package/dist/internal/c4/market.js.map +1 -1
  299. package/dist/internal/c4/mining-instructions.d.ts +2 -0
  300. package/dist/internal/c4/mining-instructions.d.ts.map +1 -0
  301. package/dist/internal/c4/mining-instructions.js +3 -0
  302. package/dist/internal/c4/mining-instructions.js.map +1 -0
  303. package/dist/internal/c4/open-signal.d.ts +8 -0
  304. package/dist/internal/c4/open-signal.d.ts.map +1 -0
  305. package/dist/internal/c4/open-signal.js +37 -0
  306. package/dist/internal/c4/open-signal.js.map +1 -0
  307. package/dist/internal/c4/pda.d.ts +43 -0
  308. package/dist/internal/c4/pda.d.ts.map +1 -1
  309. package/dist/internal/c4/pda.js +83 -17
  310. package/dist/internal/c4/pda.js.map +1 -1
  311. package/dist/internal/c4/profile-faction.d.ts +2 -1
  312. package/dist/internal/c4/profile-faction.d.ts.map +1 -1
  313. package/dist/internal/c4/profile-faction.js +36 -72
  314. package/dist/internal/c4/profile-faction.js.map +1 -1
  315. package/dist/internal/c4/profile-permissions.d.ts +1 -1
  316. package/dist/internal/c4/profile-permissions.d.ts.map +1 -1
  317. package/dist/internal/c4/profile-permissions.js +4 -1
  318. package/dist/internal/c4/profile-permissions.js.map +1 -1
  319. package/dist/internal/c4/region-geometry.d.ts +8 -0
  320. package/dist/internal/c4/region-geometry.d.ts.map +1 -0
  321. package/dist/internal/c4/region-geometry.js +43 -0
  322. package/dist/internal/c4/region-geometry.js.map +1 -0
  323. package/dist/internal/c4/rewards.d.ts +1 -61
  324. package/dist/internal/c4/rewards.d.ts.map +1 -1
  325. package/dist/internal/c4/rewards.js +23 -203
  326. package/dist/internal/c4/rewards.js.map +1 -1
  327. package/dist/internal/c4/runtime-bindings.d.ts +4 -0
  328. package/dist/internal/c4/runtime-bindings.d.ts.map +1 -0
  329. package/dist/internal/c4/runtime-bindings.js +4 -0
  330. package/dist/internal/c4/runtime-bindings.js.map +1 -0
  331. package/dist/internal/c4/scan-detection-codecs.d.ts +3 -0
  332. package/dist/internal/c4/scan-detection-codecs.d.ts.map +1 -0
  333. package/dist/internal/c4/scan-detection-codecs.js +3 -0
  334. package/dist/internal/c4/scan-detection-codecs.js.map +1 -0
  335. package/dist/internal/c4/scan-lifecycle.d.ts +306 -0
  336. package/dist/internal/c4/scan-lifecycle.d.ts.map +1 -0
  337. package/dist/internal/c4/scan-lifecycle.js +235 -0
  338. package/dist/internal/c4/scan-lifecycle.js.map +1 -0
  339. package/dist/internal/c4/scanning.d.ts +4 -41
  340. package/dist/internal/c4/scanning.d.ts.map +1 -1
  341. package/dist/internal/c4/scanning.js +27 -108
  342. package/dist/internal/c4/scanning.js.map +1 -1
  343. package/dist/internal/c4/ship-import.d.ts +4 -0
  344. package/dist/internal/c4/ship-import.d.ts.map +1 -0
  345. package/dist/internal/c4/ship-import.js +13 -0
  346. package/dist/internal/c4/ship-import.js.map +1 -0
  347. package/dist/internal/c4/ship-stats.d.ts.map +1 -1
  348. package/dist/internal/c4/ship-stats.js +45 -43
  349. package/dist/internal/c4/ship-stats.js.map +1 -1
  350. package/dist/internal/c4/starbase.d.ts +0 -32
  351. package/dist/internal/c4/starbase.d.ts.map +1 -1
  352. package/dist/internal/c4/starbase.js +26 -136
  353. package/dist/internal/c4/starbase.js.map +1 -1
  354. package/dist/internal/c4/wallet-cargo-cache.js +2 -1
  355. package/dist/internal/c4/wallet-cargo-cache.js.map +1 -1
  356. package/dist/internal/c4/world.d.ts +37 -7
  357. package/dist/internal/c4/world.d.ts.map +1 -1
  358. package/dist/internal/c4/world.js +87 -129
  359. package/dist/internal/c4/world.js.map +1 -1
  360. package/dist/internal/cargo.d.ts +2 -0
  361. package/dist/internal/cargo.d.ts.map +1 -1
  362. package/dist/internal/cargo.js +8 -0
  363. package/dist/internal/cargo.js.map +1 -1
  364. package/dist/internal/fleet.d.ts +1 -0
  365. package/dist/internal/fleet.d.ts.map +1 -1
  366. package/dist/internal/fleet.js +3 -1
  367. package/dist/internal/fleet.js.map +1 -1
  368. package/dist/internal/loyalty-atlas-bank-state.d.ts +16 -0
  369. package/dist/internal/loyalty-atlas-bank-state.d.ts.map +1 -0
  370. package/dist/internal/loyalty-atlas-bank-state.js +10 -0
  371. package/dist/internal/loyalty-atlas-bank-state.js.map +1 -0
  372. package/dist/internal/planning/account-state.d.ts +6 -0
  373. package/dist/internal/planning/account-state.d.ts.map +1 -1
  374. package/dist/internal/planning/account-state.js +13 -0
  375. package/dist/internal/planning/account-state.js.map +1 -1
  376. package/dist/internal/planning/action-runtime.d.ts +38 -0
  377. package/dist/internal/planning/action-runtime.d.ts.map +1 -0
  378. package/dist/internal/planning/action-runtime.js +63 -0
  379. package/dist/internal/planning/action-runtime.js.map +1 -0
  380. package/dist/internal/planning/arrival-guards.d.ts +19 -0
  381. package/dist/internal/planning/arrival-guards.d.ts.map +1 -0
  382. package/dist/internal/planning/arrival-guards.js +172 -0
  383. package/dist/internal/planning/arrival-guards.js.map +1 -0
  384. package/dist/internal/planning/authorization-adapter.d.ts +55 -37
  385. package/dist/internal/planning/authorization-adapter.d.ts.map +1 -1
  386. package/dist/internal/planning/authorization-adapter.js +231 -81
  387. package/dist/internal/planning/authorization-adapter.js.map +1 -1
  388. package/dist/internal/planning/character-registration-guard.d.ts +4 -0
  389. package/dist/internal/planning/character-registration-guard.d.ts.map +1 -0
  390. package/dist/internal/planning/character-registration-guard.js +59 -0
  391. package/dist/internal/planning/character-registration-guard.js.map +1 -0
  392. package/dist/internal/planning/character-registration.d.ts +23 -0
  393. package/dist/internal/planning/character-registration.d.ts.map +1 -0
  394. package/dist/internal/planning/character-registration.js +87 -0
  395. package/dist/internal/planning/character-registration.js.map +1 -0
  396. package/dist/internal/planning/currency-cache-guard.d.ts +4 -0
  397. package/dist/internal/planning/currency-cache-guard.d.ts.map +1 -0
  398. package/dist/internal/planning/currency-cache-guard.js +34 -0
  399. package/dist/internal/planning/currency-cache-guard.js.map +1 -0
  400. package/dist/internal/planning/democracy-history.d.ts +50 -0
  401. package/dist/internal/planning/democracy-history.d.ts.map +1 -0
  402. package/dist/internal/planning/democracy-history.js +189 -0
  403. package/dist/internal/planning/democracy-history.js.map +1 -0
  404. package/dist/internal/planning/direct-options.d.ts +17 -0
  405. package/dist/internal/planning/direct-options.d.ts.map +1 -0
  406. package/dist/internal/planning/direct-options.js +43 -0
  407. package/dist/internal/planning/direct-options.js.map +1 -0
  408. package/dist/internal/planning/faction-enrollment.d.ts +0 -4
  409. package/dist/internal/planning/faction-enrollment.d.ts.map +1 -1
  410. package/dist/internal/planning/faction-enrollment.js +1 -31
  411. package/dist/internal/planning/faction-enrollment.js.map +1 -1
  412. package/dist/internal/planning/faction-trade-guards.d.ts +30 -0
  413. package/dist/internal/planning/faction-trade-guards.d.ts.map +1 -0
  414. package/dist/internal/planning/faction-trade-guards.js +15 -0
  415. package/dist/internal/planning/faction-trade-guards.js.map +1 -0
  416. package/dist/internal/planning/faction-trade-state.d.ts +16 -0
  417. package/dist/internal/planning/faction-trade-state.d.ts.map +1 -0
  418. package/dist/internal/planning/faction-trade-state.js +43 -0
  419. package/dist/internal/planning/faction-trade-state.js.map +1 -0
  420. package/dist/internal/planning/fleet-arrival.d.ts +40 -0
  421. package/dist/internal/planning/fleet-arrival.d.ts.map +1 -0
  422. package/dist/internal/planning/fleet-arrival.js +107 -0
  423. package/dist/internal/planning/fleet-arrival.js.map +1 -0
  424. package/dist/internal/planning/fleet-attack-xp.d.ts +20 -0
  425. package/dist/internal/planning/fleet-attack-xp.d.ts.map +1 -0
  426. package/dist/internal/planning/fleet-attack-xp.js +104 -0
  427. package/dist/internal/planning/fleet-attack-xp.js.map +1 -0
  428. package/dist/internal/planning/fleet-creation.d.ts +30 -0
  429. package/dist/internal/planning/fleet-creation.d.ts.map +1 -0
  430. package/dist/internal/planning/fleet-creation.js +89 -0
  431. package/dist/internal/planning/fleet-creation.js.map +1 -0
  432. package/dist/internal/planning/fleet-crew-guard.d.ts +28 -0
  433. package/dist/internal/planning/fleet-crew-guard.d.ts.map +1 -0
  434. package/dist/internal/planning/fleet-crew-guard.js +48 -0
  435. package/dist/internal/planning/fleet-crew-guard.js.map +1 -0
  436. package/dist/internal/planning/fleet-crew-transfer.d.ts +7 -0
  437. package/dist/internal/planning/fleet-crew-transfer.d.ts.map +1 -0
  438. package/dist/internal/planning/fleet-crew-transfer.js +123 -0
  439. package/dist/internal/planning/fleet-crew-transfer.js.map +1 -0
  440. package/dist/internal/planning/fleet-dock-funding-guard.d.ts +42 -0
  441. package/dist/internal/planning/fleet-dock-funding-guard.d.ts.map +1 -0
  442. package/dist/internal/planning/fleet-dock-funding-guard.js +209 -0
  443. package/dist/internal/planning/fleet-dock-funding-guard.js.map +1 -0
  444. package/dist/internal/planning/fleet-dock-funding.d.ts +9 -0
  445. package/dist/internal/planning/fleet-dock-funding.d.ts.map +1 -0
  446. package/dist/internal/planning/fleet-dock-funding.js +154 -0
  447. package/dist/internal/planning/fleet-dock-funding.js.map +1 -0
  448. package/dist/internal/planning/fleet-docking-guard.d.ts +4 -0
  449. package/dist/internal/planning/fleet-docking-guard.d.ts.map +1 -0
  450. package/dist/internal/planning/fleet-docking-guard.js +43 -0
  451. package/dist/internal/planning/fleet-docking-guard.js.map +1 -0
  452. package/dist/internal/planning/fleet-docking.d.ts +10 -0
  453. package/dist/internal/planning/fleet-docking.d.ts.map +1 -0
  454. package/dist/internal/planning/fleet-docking.js +71 -0
  455. package/dist/internal/planning/fleet-docking.js.map +1 -0
  456. package/dist/internal/planning/fleet-respawn.d.ts +28 -0
  457. package/dist/internal/planning/fleet-respawn.d.ts.map +1 -0
  458. package/dist/internal/planning/fleet-respawn.js +164 -0
  459. package/dist/internal/planning/fleet-respawn.js.map +1 -0
  460. package/dist/internal/planning/fleet-ship-assembly.d.ts +36 -0
  461. package/dist/internal/planning/fleet-ship-assembly.d.ts.map +1 -0
  462. package/dist/internal/planning/fleet-ship-assembly.js +118 -0
  463. package/dist/internal/planning/fleet-ship-assembly.js.map +1 -0
  464. package/dist/internal/planning/fleet-state.d.ts +25 -0
  465. package/dist/internal/planning/fleet-state.d.ts.map +1 -0
  466. package/dist/internal/planning/fleet-state.js +28 -0
  467. package/dist/internal/planning/fleet-state.js.map +1 -0
  468. package/dist/internal/planning/governance-fees.d.ts +64 -0
  469. package/dist/internal/planning/governance-fees.d.ts.map +1 -0
  470. package/dist/internal/planning/governance-fees.js +231 -0
  471. package/dist/internal/planning/governance-fees.js.map +1 -0
  472. package/dist/internal/planning/local-market-order-funding.d.ts +8 -0
  473. package/dist/internal/planning/local-market-order-funding.d.ts.map +1 -0
  474. package/dist/internal/planning/local-market-order-funding.js +128 -0
  475. package/dist/internal/planning/local-market-order-funding.js.map +1 -0
  476. package/dist/internal/planning/local-market-order.d.ts +40 -0
  477. package/dist/internal/planning/local-market-order.d.ts.map +1 -0
  478. package/dist/internal/planning/local-market-order.js +139 -0
  479. package/dist/internal/planning/local-market-order.js.map +1 -0
  480. package/dist/internal/planning/mining-stop.d.ts +14 -0
  481. package/dist/internal/planning/mining-stop.d.ts.map +1 -0
  482. package/dist/internal/planning/mining-stop.js +42 -0
  483. package/dist/internal/planning/mining-stop.js.map +1 -0
  484. package/dist/internal/planning/movement-character.d.ts +7 -0
  485. package/dist/internal/planning/movement-character.d.ts.map +1 -0
  486. package/dist/internal/planning/movement-character.js +41 -0
  487. package/dist/internal/planning/movement-character.js.map +1 -0
  488. package/dist/internal/planning/movement-xp-guard.d.ts +4 -0
  489. package/dist/internal/planning/movement-xp-guard.d.ts.map +1 -0
  490. package/dist/internal/planning/movement-xp-guard.js +104 -0
  491. package/dist/internal/planning/movement-xp-guard.js.map +1 -0
  492. package/dist/internal/planning/open-signal-guard.d.ts +3 -0
  493. package/dist/internal/planning/open-signal-guard.d.ts.map +1 -0
  494. package/dist/internal/planning/open-signal-guard.js +32 -0
  495. package/dist/internal/planning/open-signal-guard.js.map +1 -0
  496. package/dist/internal/planning/open-signal.d.ts +31 -0
  497. package/dist/internal/planning/open-signal.d.ts.map +1 -0
  498. package/dist/internal/planning/open-signal.js +85 -0
  499. package/dist/internal/planning/open-signal.js.map +1 -0
  500. package/dist/internal/planning/original-token.d.ts +40 -0
  501. package/dist/internal/planning/original-token.d.ts.map +1 -0
  502. package/dist/internal/planning/original-token.js +112 -0
  503. package/dist/internal/planning/original-token.js.map +1 -0
  504. package/dist/internal/planning/own-starbase-facts.d.ts +33 -0
  505. package/dist/internal/planning/own-starbase-facts.d.ts.map +1 -0
  506. package/dist/internal/planning/own-starbase-facts.js +49 -0
  507. package/dist/internal/planning/own-starbase-facts.js.map +1 -0
  508. package/dist/internal/planning/plan-fields.d.ts +60 -0
  509. package/dist/internal/planning/plan-fields.d.ts.map +1 -0
  510. package/dist/internal/planning/plan-fields.js +159 -0
  511. package/dist/internal/planning/plan-fields.js.map +1 -0
  512. package/dist/internal/planning/plan-guards.d.ts +4 -0
  513. package/dist/internal/planning/plan-guards.d.ts.map +1 -0
  514. package/dist/internal/planning/plan-guards.js +175 -0
  515. package/dist/internal/planning/plan-guards.js.map +1 -0
  516. package/dist/internal/planning/profile-key.d.ts +13 -0
  517. package/dist/internal/planning/profile-key.d.ts.map +1 -0
  518. package/dist/internal/planning/profile-key.js +20 -0
  519. package/dist/internal/planning/profile-key.js.map +1 -0
  520. package/dist/internal/planning/recipe-state.d.ts +9 -0
  521. package/dist/internal/planning/recipe-state.d.ts.map +1 -0
  522. package/dist/internal/planning/recipe-state.js +23 -0
  523. package/dist/internal/planning/recipe-state.js.map +1 -0
  524. package/dist/internal/planning/rent-catalog.d.ts +37 -0
  525. package/dist/internal/planning/rent-catalog.d.ts.map +1 -0
  526. package/dist/internal/planning/rent-catalog.js +165 -0
  527. package/dist/internal/planning/rent-catalog.js.map +1 -0
  528. package/dist/internal/planning/rent.d.ts +19 -0
  529. package/dist/internal/planning/rent.d.ts.map +1 -0
  530. package/dist/internal/planning/rent.js +35 -0
  531. package/dist/internal/planning/rent.js.map +1 -0
  532. package/dist/internal/planning/research-unlock.d.ts +60 -0
  533. package/dist/internal/planning/research-unlock.d.ts.map +1 -0
  534. package/dist/internal/planning/research-unlock.js +208 -0
  535. package/dist/internal/planning/research-unlock.js.map +1 -0
  536. package/dist/internal/planning/respawn-guards.d.ts +3 -0
  537. package/dist/internal/planning/respawn-guards.d.ts.map +1 -0
  538. package/dist/internal/planning/respawn-guards.js +42 -0
  539. package/dist/internal/planning/respawn-guards.js.map +1 -0
  540. package/dist/internal/planning/runtime-guards.d.ts +41 -0
  541. package/dist/internal/planning/runtime-guards.d.ts.map +1 -0
  542. package/dist/internal/planning/runtime-guards.js +658 -0
  543. package/dist/internal/planning/runtime-guards.js.map +1 -0
  544. package/dist/internal/planning/scan-completion-guard.d.ts +43 -0
  545. package/dist/internal/planning/scan-completion-guard.d.ts.map +1 -0
  546. package/dist/internal/planning/scan-completion-guard.js +95 -0
  547. package/dist/internal/planning/scan-completion-guard.js.map +1 -0
  548. package/dist/internal/planning/scan-completion.d.ts +20 -0
  549. package/dist/internal/planning/scan-completion.d.ts.map +1 -0
  550. package/dist/internal/planning/scan-completion.js +197 -0
  551. package/dist/internal/planning/scan-completion.js.map +1 -0
  552. package/dist/internal/planning/scan-detect-guard.d.ts +34 -0
  553. package/dist/internal/planning/scan-detect-guard.d.ts.map +1 -0
  554. package/dist/internal/planning/scan-detect-guard.js +77 -0
  555. package/dist/internal/planning/scan-detect-guard.js.map +1 -0
  556. package/dist/internal/planning/scan-detect.d.ts +24 -0
  557. package/dist/internal/planning/scan-detect.d.ts.map +1 -0
  558. package/dist/internal/planning/scan-detect.js +245 -0
  559. package/dist/internal/planning/scan-detect.js.map +1 -0
  560. package/dist/internal/planning/scan-input.d.ts +9 -0
  561. package/dist/internal/planning/scan-input.d.ts.map +1 -0
  562. package/dist/internal/planning/scan-input.js +58 -0
  563. package/dist/internal/planning/scan-input.js.map +1 -0
  564. package/dist/internal/planning/ship-import-funding.d.ts +22 -0
  565. package/dist/internal/planning/ship-import-funding.d.ts.map +1 -0
  566. package/dist/internal/planning/ship-import-funding.js +53 -0
  567. package/dist/internal/planning/ship-import-funding.js.map +1 -0
  568. package/dist/internal/planning/ship-token.d.ts +1 -6
  569. package/dist/internal/planning/ship-token.d.ts.map +1 -1
  570. package/dist/internal/planning/ship-token.js +2 -41
  571. package/dist/internal/planning/ship-token.js.map +1 -1
  572. package/dist/internal/planning/starbase-player-owner.d.ts +21 -0
  573. package/dist/internal/planning/starbase-player-owner.d.ts.map +1 -0
  574. package/dist/internal/planning/starbase-player-owner.js +17 -0
  575. package/dist/internal/planning/starbase-player-owner.js.map +1 -0
  576. package/dist/internal/planning/starbase-system-guard.d.ts +6 -0
  577. package/dist/internal/planning/starbase-system-guard.d.ts.map +1 -0
  578. package/dist/internal/planning/starbase-system-guard.js +94 -0
  579. package/dist/internal/planning/starbase-system-guard.js.map +1 -0
  580. package/dist/internal/planning/starbase-system.d.ts +15 -0
  581. package/dist/internal/planning/starbase-system.d.ts.map +1 -0
  582. package/dist/internal/planning/starbase-system.js +25 -0
  583. package/dist/internal/planning/starbase-system.js.map +1 -0
  584. package/dist/internal/planning/token-mint.d.ts +4 -0
  585. package/dist/internal/planning/token-mint.d.ts.map +1 -0
  586. package/dist/internal/planning/token-mint.js +16 -0
  587. package/dist/internal/planning/token-mint.js.map +1 -0
  588. package/dist/internal/planning/trade-faction-market.d.ts +20 -0
  589. package/dist/internal/planning/trade-faction-market.d.ts.map +1 -0
  590. package/dist/internal/planning/trade-faction-market.js +56 -0
  591. package/dist/internal/planning/trade-faction-market.js.map +1 -0
  592. package/dist/internal/planning/wallet-starbase.d.ts +34 -0
  593. package/dist/internal/planning/wallet-starbase.d.ts.map +1 -0
  594. package/dist/internal/planning/wallet-starbase.js +39 -0
  595. package/dist/internal/planning/wallet-starbase.js.map +1 -0
  596. package/dist/internal/planning/xp-runtime.d.ts +36 -0
  597. package/dist/internal/planning/xp-runtime.d.ts.map +1 -0
  598. package/dist/internal/planning/xp-runtime.js +117 -0
  599. package/dist/internal/planning/xp-runtime.js.map +1 -0
  600. package/dist/internal/read-meta.d.ts +12 -0
  601. package/dist/internal/read-meta.d.ts.map +1 -1
  602. package/dist/internal/read-meta.js +10 -0
  603. package/dist/internal/read-meta.js.map +1 -1
  604. package/dist/internal/system-program.d.ts +4 -0
  605. package/dist/internal/system-program.d.ts.map +1 -0
  606. package/dist/internal/system-program.js +3 -0
  607. package/dist/internal/system-program.js.map +1 -0
  608. package/dist/loyalty/actions/index.d.ts +1 -1
  609. package/dist/loyalty/actions/index.d.ts.map +1 -1
  610. package/dist/loyalty/actions/index.js +15 -45
  611. package/dist/loyalty/actions/index.js.map +1 -1
  612. package/dist/loyalty/index.d.ts.map +1 -1
  613. package/dist/loyalty/index.js +6 -18
  614. package/dist/loyalty/index.js.map +1 -1
  615. package/dist/markets/actions/index.d.ts +62 -4
  616. package/dist/markets/actions/index.d.ts.map +1 -1
  617. package/dist/markets/actions/index.js +230 -178
  618. package/dist/markets/actions/index.js.map +1 -1
  619. package/dist/markets/actions/trade-faction-market.d.ts +43 -0
  620. package/dist/markets/actions/trade-faction-market.d.ts.map +1 -0
  621. package/dist/markets/actions/trade-faction-market.js +277 -0
  622. package/dist/markets/actions/trade-faction-market.js.map +1 -0
  623. package/dist/markets/index.d.ts +38 -7
  624. package/dist/markets/index.d.ts.map +1 -1
  625. package/dist/markets/index.js +59 -20
  626. package/dist/markets/index.js.map +1 -1
  627. package/dist/mining/actions/index.d.ts +87 -0
  628. package/dist/mining/actions/index.d.ts.map +1 -0
  629. package/dist/mining/actions/index.js +319 -0
  630. package/dist/mining/actions/index.js.map +1 -0
  631. package/dist/mining/estimate.d.ts +114 -0
  632. package/dist/mining/estimate.d.ts.map +1 -0
  633. package/dist/mining/estimate.js +127 -0
  634. package/dist/mining/estimate.js.map +1 -0
  635. package/dist/mining/index.d.ts +9 -2
  636. package/dist/mining/index.d.ts.map +1 -1
  637. package/dist/mining/index.js +10 -13
  638. package/dist/mining/index.js.map +1 -1
  639. package/dist/mining/profile.d.ts +82 -0
  640. package/dist/mining/profile.d.ts.map +1 -0
  641. package/dist/mining/profile.js +109 -0
  642. package/dist/mining/profile.js.map +1 -0
  643. package/dist/planning/assembly.d.ts +6 -1
  644. package/dist/planning/assembly.d.ts.map +1 -1
  645. package/dist/planning/assembly.js +12 -8
  646. package/dist/planning/assembly.js.map +1 -1
  647. package/dist/planning/execution.d.ts +19 -3
  648. package/dist/planning/execution.d.ts.map +1 -1
  649. package/dist/planning/execution.js +540 -72
  650. package/dist/planning/execution.js.map +1 -1
  651. package/dist/planning/index.d.ts +217 -149
  652. package/dist/planning/index.d.ts.map +1 -1
  653. package/dist/planning/index.js +31 -355
  654. package/dist/planning/index.js.map +1 -1
  655. package/dist/planning/plan-construction.d.ts +25 -0
  656. package/dist/planning/plan-construction.d.ts.map +1 -0
  657. package/dist/planning/plan-construction.js +143 -0
  658. package/dist/planning/plan-construction.js.map +1 -0
  659. package/dist/planning/plan-sequence-checkpoint.d.ts +9 -0
  660. package/dist/planning/plan-sequence-checkpoint.d.ts.map +1 -1
  661. package/dist/planning/plan-sequence-checkpoint.js +16 -7
  662. package/dist/planning/plan-sequence-checkpoint.js.map +1 -1
  663. package/dist/planning/plan-sequence-execution.d.ts +5 -0
  664. package/dist/planning/plan-sequence-execution.d.ts.map +1 -1
  665. package/dist/planning/plan-sequence-execution.js +74 -40
  666. package/dist/planning/plan-sequence-execution.js.map +1 -1
  667. package/dist/planning/plan-sequence.d.ts +2 -1
  668. package/dist/planning/plan-sequence.d.ts.map +1 -1
  669. package/dist/planning/plan-sequence.js +8 -1
  670. package/dist/planning/plan-sequence.js.map +1 -1
  671. package/dist/planning/plan-validation.d.ts +17 -0
  672. package/dist/planning/plan-validation.d.ts.map +1 -0
  673. package/dist/planning/plan-validation.js +135 -0
  674. package/dist/planning/plan-validation.js.map +1 -0
  675. package/dist/planning/simulation.d.ts +25 -1
  676. package/dist/planning/simulation.d.ts.map +1 -1
  677. package/dist/planning/simulation.js +111 -11
  678. package/dist/planning/simulation.js.map +1 -1
  679. package/dist/registry/cargo.d.ts +7 -12
  680. package/dist/registry/cargo.d.ts.map +1 -1
  681. package/dist/registry/cargo.js +20 -20
  682. package/dist/registry/cargo.js.map +1 -1
  683. package/dist/registry/docking.d.ts +10 -0
  684. package/dist/registry/docking.d.ts.map +1 -0
  685. package/dist/registry/docking.js +12 -0
  686. package/dist/registry/docking.js.map +1 -0
  687. package/dist/registry/index.d.ts +4 -0
  688. package/dist/registry/index.d.ts.map +1 -1
  689. package/dist/registry/index.js +190 -224
  690. package/dist/registry/index.js.map +1 -1
  691. package/dist/registry/onboarding.d.ts +10 -2
  692. package/dist/registry/onboarding.d.ts.map +1 -1
  693. package/dist/registry/onboarding.js +20 -7
  694. package/dist/registry/onboarding.js.map +1 -1
  695. package/dist/registry/scanning.d.ts +7 -0
  696. package/dist/registry/scanning.d.ts.map +1 -0
  697. package/dist/registry/scanning.js +13 -0
  698. package/dist/registry/scanning.js.map +1 -0
  699. package/dist/registry/ship-stats-snapshot.js +19 -19
  700. package/dist/registry/ship-stats-snapshot.js.map +1 -1
  701. package/dist/registry/ships.d.ts +22 -2
  702. package/dist/registry/ships.d.ts.map +1 -1
  703. package/dist/registry/ships.js +31 -4
  704. package/dist/registry/ships.js.map +1 -1
  705. package/dist/registry/xp-runtime.d.ts +4 -0
  706. package/dist/registry/xp-runtime.d.ts.map +1 -0
  707. package/dist/registry/xp-runtime.js +14 -0
  708. package/dist/registry/xp-runtime.js.map +1 -0
  709. package/dist/rewards/index.d.ts +11 -5
  710. package/dist/rewards/index.d.ts.map +1 -1
  711. package/dist/rewards/index.js +10 -21
  712. package/dist/rewards/index.js.map +1 -1
  713. package/dist/scanning/actions/index.d.ts +116 -0
  714. package/dist/scanning/actions/index.d.ts.map +1 -0
  715. package/dist/scanning/actions/index.js +191 -0
  716. package/dist/scanning/actions/index.js.map +1 -0
  717. package/dist/scanning/index.d.ts +9 -12
  718. package/dist/scanning/index.d.ts.map +1 -1
  719. package/dist/scanning/index.js +18 -22
  720. package/dist/scanning/index.js.map +1 -1
  721. package/dist/scanning/lifecycle.d.ts +77 -0
  722. package/dist/scanning/lifecycle.d.ts.map +1 -0
  723. package/dist/scanning/lifecycle.js +93 -0
  724. package/dist/scanning/lifecycle.js.map +1 -0
  725. package/dist/scanning/odds.d.ts +111 -0
  726. package/dist/scanning/odds.d.ts.map +1 -0
  727. package/dist/scanning/odds.js +294 -0
  728. package/dist/scanning/odds.js.map +1 -0
  729. package/dist/scanning/signals.d.ts +121 -0
  730. package/dist/scanning/signals.d.ts.map +1 -0
  731. package/dist/scanning/signals.js +171 -0
  732. package/dist/scanning/signals.js.map +1 -0
  733. package/dist/starbases/actions/index.d.ts +6 -4
  734. package/dist/starbases/actions/index.d.ts.map +1 -1
  735. package/dist/starbases/actions/index.js +34 -67
  736. package/dist/starbases/actions/index.js.map +1 -1
  737. package/dist/starbases/index.d.ts.map +1 -1
  738. package/dist/starbases/index.js +3 -11
  739. package/dist/starbases/index.js.map +1 -1
  740. package/dist/world/index.d.ts +108 -5
  741. package/dist/world/index.d.ts.map +1 -1
  742. package/dist/world/index.js +110 -15
  743. package/dist/world/index.js.map +1 -1
  744. package/dist/world/planet-claim-stake-plots.d.ts +139 -0
  745. package/dist/world/planet-claim-stake-plots.d.ts.map +1 -0
  746. package/dist/world/planet-claim-stake-plots.js +117 -0
  747. package/dist/world/planet-claim-stake-plots.js.map +1 -0
  748. package/dist/world/starbase-status.d.ts +176 -0
  749. package/dist/world/starbase-status.d.ts.map +1 -0
  750. package/dist/world/starbase-status.js +153 -0
  751. package/dist/world/starbase-status.js.map +1 -0
  752. package/package.json +10 -2
@@ -1,21 +1,45 @@
1
- import { getAddressEncoder, getBytesEncoder, getProgramDerivedAddress, isAddress, } from '@solana/kit';
1
+ import { deriveCurrencyCache, validateCurrencyCache, } from '../../internal/c4/currency-cache.js';
2
+ import { isAddress } from '@solana/kit';
2
3
  import { InvalidActionInputError, SageSdkError } from '../../client/errors.js';
3
4
  import { readContextRawAccounts } from '../../client/reconciliation.js';
4
- import { sage } from '../../internal/c4/bindings.js';
5
+ import { StarbaseLevel } from '../../internal/c4/runtime-bindings.js';
5
6
  import { readFreshGameClaimStakeConfiguration, } from '../../registry/claim-stakes.js';
6
- import { characterAccountDefinition, findCharacterPda, } from '../../internal/c4/identity.js';
7
+ import { characterAccountDefinition, findCharacterPda, } from '../../internal/c4/character.js';
7
8
  import { deriveFactionOwnershipAddress, factionOwnershipAccountDefinition, } from '../../internal/c4/factions.js';
8
9
  import { claimStakeAccountDefinition } from '../../internal/c4/claim-stake.js';
9
- import { deriveFleetCrewBindingAddress, fleetCrewBindingAccountDefinition, } from '../../internal/c4/fleet-crew-binding.js';
10
+ import { CLAIM_STAKE_PLOT_ROWS, claimStakePlacementRow, claimStakePlotPositions, } from '../../internal/c4/claim-stake-plots.js';
11
+ import { factionValue } from '../../internal/c4/faction.js';
12
+ import { deriveFleetCrewBindingAddress, fleetCrewBindingAccountDefinition, matchesFleetCrewBindingIdentity, } from '../../internal/c4/fleet-crew-binding.js';
10
13
  import { deriveFleetAddress, fleetAccountDefinition, } from '../../internal/c4/fleet.js';
11
14
  import { deriveProfileFactionAddress, profileFactionIdentityAccountDefinition, } from '../../internal/c4/profile-faction.js';
12
15
  import { deriveStarbasePlayerAddress, starbasePlayerAccountDefinition, } from '../../internal/c4/starbase.js';
13
- import { celestialBodyAccountDefinition, decodeIndexedCelestialBodyAccount, deriveCelestialBodyAddress, deriveStarSystemAddress, starSystemAccountDefinition, } from '../../internal/c4/world.js';
16
+ import { celestialBodyAccountDefinition, deriveCelestialBodyAddress, deriveStarSystemAddress, starSystemAccountDefinition, } from '../../internal/c4/world.js';
17
+ import { resolveXpRuntime } from '../../internal/planning/xp-runtime.js';
18
+ import { requireDisabledStakeRent, requireNewStakeRent, } from '../../internal/planning/rent-catalog.js';
19
+ import { deriveStructureXpAccrualAddress } from '../../internal/c4/pda.js';
20
+ import { requireProfileStarbasePlayer } from '../../internal/planning/starbase-player-owner.js';
14
21
  import { requireGameAddress } from '../../internal/game-context.js';
15
- import { adaptCancelClaimStakeBuildingChangesInstruction, adaptClaimStakeBuildingChangesInstruction, adaptDeconstructClaimStakeInstruction, adaptFinalizeClaimStakeBuildingChangesInstruction, adaptPlaceClaimStakeInstruction, adaptRecoverClaimStakeFleetTransferInstruction, adaptStartClaimStakeFleetTransferInstruction, adaptTopUpClaimStakeRentInstruction, validatePlanAuthorization, } from '../../internal/planning/authorization-adapter.js';
16
- import { createPlan, } from '../../planning/index.js';
22
+ import { cargoStorageUnits } from '../../internal/cargo.js';
23
+ import { adaptCancelClaimStakeBuildingChangesInstruction, adaptClaimStakeBuildingChangesInstruction, adaptDeconstructClaimStakeInstruction, adaptFinalizeClaimStakeBuildingChangesInstruction, adaptPlaceClaimStakeInstruction, adaptRecoverClaimStakeFleetTransferInstruction, adaptStartClaimStakeFleetTransferInstruction, adaptTopUpClaimStakeRentInstruction, readPlanAuthorization, } from '../../internal/planning/authorization-adapter.js';
24
+ import { buildPlannerPlan } from '../../planning/plan-construction.js';
17
25
  import { readFreshGameCargoDefinitions } from '../../registry/cargo.js';
18
26
  import { resolveCargo } from '../../registry/index.js';
27
+ import { accountAbsentPrecondition, accountStatePrecondition, } from '../../internal/planning/account-state.js';
28
+ async function resolveClaimStakeXp(ctx, game, structure, character) {
29
+ const xp = await resolveXpRuntime(ctx, {
30
+ game,
31
+ structure,
32
+ ...(character === undefined ? {} : { character }),
33
+ });
34
+ const guard = {
35
+ kind: 'xp-runtime',
36
+ address: game,
37
+ intent: xp.intent,
38
+ accounts: xp.accounts,
39
+ describes: 'The Game and canonical Claim Stake XP accounts remain supported; accrued XP may change. Initialize or repair missing XP state using the game client, then recreate the Plan.',
40
+ };
41
+ return { accounts: xp.accounts, guard };
42
+ }
19
43
  const U64_MAX = (1n << 64n) - 1n;
20
44
  const U16_MAX = 65_535;
21
45
  const I32_MAX = 2_147_483_647;
@@ -31,13 +55,13 @@ const LEVEL_INDEX = Object.freeze({
31
55
  css: 6,
32
56
  });
33
57
  const CLAIM_STAKE_PLOT_LEVELS = Object.freeze([
34
- sage.StarbaseLevel.Level0,
35
- sage.StarbaseLevel.Level1,
36
- sage.StarbaseLevel.Level2,
37
- sage.StarbaseLevel.Level3,
38
- sage.StarbaseLevel.Level4,
39
- sage.StarbaseLevel.Level5,
40
- sage.StarbaseLevel.Css,
58
+ StarbaseLevel.Level0,
59
+ StarbaseLevel.Level1,
60
+ StarbaseLevel.Level2,
61
+ StarbaseLevel.Level3,
62
+ StarbaseLevel.Level4,
63
+ StarbaseLevel.Level5,
64
+ StarbaseLevel.Css,
41
65
  ]);
42
66
  const TIER_INDEX = Object.freeze({
43
67
  one: 0,
@@ -90,15 +114,7 @@ function validateOptions(value) {
90
114
  'hubBuildingId',
91
115
  'initialRentAmount',
92
116
  ]);
93
- const authorizationRecord = record(options.authorization, 'authorization');
94
- exactKeys(authorizationRecord, 'authorization', ['profile', 'authority', 'keyIndex'], ['certificate']);
95
- const authorization = authorizationRecord;
96
- address(authorization.profile, 'authorization.profile');
97
- address(authorization.authority, 'authorization.authority');
98
- if (authorization.certificate !== undefined) {
99
- address(authorization.certificate, 'authorization.certificate');
100
- }
101
- validatePlanAuthorization(authorization);
117
+ const authorization = readPlanAuthorization(options.authorization);
102
118
  if (typeof options.initialRentAmount !== 'bigint' ||
103
119
  options.initialRentAmount <= 0n ||
104
120
  options.initialRentAmount > U64_MAX) {
@@ -112,22 +128,10 @@ function validateOptions(value) {
112
128
  initialRentAmount: options.initialRentAmount,
113
129
  };
114
130
  }
115
- function validateAuthorization(value) {
116
- const authorizationRecord = record(value, 'authorization');
117
- exactKeys(authorizationRecord, 'authorization', ['profile', 'authority', 'keyIndex'], ['certificate']);
118
- const authorization = authorizationRecord;
119
- address(authorization.profile, 'authorization.profile');
120
- address(authorization.authority, 'authorization.authority');
121
- if (authorization.certificate !== undefined) {
122
- address(authorization.certificate, 'authorization.certificate');
123
- }
124
- validatePlanAuthorization(authorization);
125
- return authorization;
126
- }
127
131
  function validateBuildingChangeOptions(value) {
128
132
  const options = record(value, 'options');
129
133
  exactKeys(options, 'options', ['authorization', 'buildingChanges']);
130
- const authorization = validateAuthorization(options.authorization);
134
+ const authorization = readPlanAuthorization(options.authorization);
131
135
  const values = options.buildingChanges;
132
136
  if (!Array.isArray(values) || values.length === 0) {
133
137
  invalid('buildingChanges', values, 'a non-empty dense array of add/remove building changes', 'Provide at least one exact building change before retrying.');
@@ -168,22 +172,22 @@ function validateBuildingChangeOptions(value) {
168
172
  function validateFinalizeBuildingChangeOptions(value) {
169
173
  const options = record(value, 'options');
170
174
  exactKeys(options, 'options', ['authorization']);
171
- return { authorization: validateAuthorization(options.authorization) };
175
+ return { authorization: readPlanAuthorization(options.authorization) };
172
176
  }
173
177
  function validateCancelBuildingChangeOptions(value) {
174
178
  const options = record(value, 'options');
175
179
  exactKeys(options, 'options', ['authorization']);
176
- return { authorization: validateAuthorization(options.authorization) };
180
+ return { authorization: readPlanAuthorization(options.authorization) };
177
181
  }
178
182
  function validateDeconstructOptions(value) {
179
183
  const options = record(value, 'options');
180
184
  exactKeys(options, 'options', ['authorization']);
181
- return { authorization: validateAuthorization(options.authorization) };
185
+ return { authorization: readPlanAuthorization(options.authorization) };
182
186
  }
183
187
  function validateTopUpRentOptions(value) {
184
188
  const options = record(value, 'options');
185
189
  exactKeys(options, 'options', ['amount', 'authorization']);
186
- const authorization = validateAuthorization(options.authorization);
190
+ const authorization = readPlanAuthorization(options.authorization);
187
191
  if (typeof options.amount !== 'bigint' ||
188
192
  options.amount <= 0n ||
189
193
  options.amount > U64_MAX) {
@@ -220,7 +224,7 @@ function validateCargoRequests(value, field, seen) {
220
224
  function validateStartFleetTransferOptions(value) {
221
225
  const options = record(value, 'options');
222
226
  exactKeys(options, 'options', ['authorization', 'load', 'unload']);
223
- const authorization = validateAuthorization(options.authorization);
227
+ const authorization = readPlanAuthorization(options.authorization);
224
228
  const seen = new Set();
225
229
  const load = validateCargoRequests(options.load, 'load', seen);
226
230
  const unload = validateCargoRequests(options.unload, 'unload', seen);
@@ -232,7 +236,7 @@ function validateStartFleetTransferOptions(value) {
232
236
  function validateRecoverFleetTransferOptions(value) {
233
237
  const options = record(value, 'options');
234
238
  exactKeys(options, 'options', ['atUnixSeconds', 'authorization']);
235
- const authorization = validateAuthorization(options.authorization);
239
+ const authorization = readPlanAuthorization(options.authorization);
236
240
  if (typeof options.atUnixSeconds !== 'bigint' ||
237
241
  options.atUnixSeconds < 0n ||
238
242
  options.atUnixSeconds > U64_MAX) {
@@ -240,53 +244,9 @@ function validateRecoverFleetTransferOptions(value) {
240
244
  }
241
245
  return { atUnixSeconds: options.atUnixSeconds, authorization };
242
246
  }
243
- async function deriveCurrencyCacheAddress(game) {
244
- const [derived] = await getProgramDerivedAddress({
245
- programAddress: sage.SAGE_PROGRAM_ADDRESS,
246
- seeds: [
247
- getBytesEncoder().encode(new TextEncoder().encode('CurrencyConfig')),
248
- getAddressEncoder().encode(game),
249
- ],
250
- });
251
- return derived;
252
- }
253
- function validateCurrencyCache(account, game, canonicalAddress, action) {
254
- const decoder = sage.getCurrencyConfigCacheDecoder();
255
- const validDiscriminator = sage.CURRENCY_CONFIG_CACHE_DISCRIMINATOR.every((byte, index) => account.data[index] === byte);
256
- if (account.address !== canonicalAddress ||
257
- account.owner !== sage.SAGE_PROGRAM_ADDRESS ||
258
- account.data.length !== decoder.fixedSize ||
259
- !validDiscriminator ||
260
- decoder.decode(account.data).gameId !== game) {
261
- fail(`CurrencyCache ${account.address} does not match the configured Game, canonical PDA, owner, or pinned account layout. Refresh the Game currency configuration and retry ${action}.`, account.address);
262
- }
263
- }
264
247
  function requireAccount(account, accountAddress, message) {
265
248
  return account ?? fail(message, accountAddress);
266
249
  }
267
- async function hashAccountState(account) {
268
- const owner = getAddressEncoder().encode(account.owner);
269
- const canonical = new Uint8Array(owner.length + account.data.length);
270
- canonical.set(owner);
271
- canonical.set(account.data, owner.length);
272
- const digest = new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', canonical));
273
- return [...digest].map((byte) => byte.toString(16).padStart(2, '0')).join('');
274
- }
275
- async function precondition(account, describes) {
276
- return Object.freeze({
277
- address: account.address,
278
- dataHash: await hashAccountState(account),
279
- describes,
280
- kind: 'account-state',
281
- });
282
- }
283
- function absentPrecondition(accountAddress, describes) {
284
- return Object.freeze({
285
- address: accountAddress,
286
- describes,
287
- kind: 'account-absent',
288
- });
289
- }
290
250
  function findDefinition(configuration, id, game) {
291
251
  const definition = configuration.claimStakes.definitions.find((candidate) => candidate.id === id);
292
252
  return (definition ??
@@ -297,10 +257,6 @@ function findHub(configuration, id, game) {
297
257
  return (hub ??
298
258
  fail(`Hub building ${String(id)} is not a member of the configured Game building catalog. Choose an exact current bundled hub id, refresh the Game definitions, and retry.`, game));
299
259
  }
300
- function claimedCount(details, definitionId) {
301
- return (details.claimedPlots.find(({ key }) => key === definitionId)?.structures
302
- .length ?? 0);
303
- }
304
260
  /** Plans placement of one Claim Stake with its exact bundled hub.
305
261
  *
306
262
  * The planner validates the configured Game/Profile/Character/System/Body
@@ -309,6 +265,9 @@ function claimedCount(details, definitionId) {
309
265
  * permissions, placement races, rent arithmetic, and program capacity remain
310
266
  * chain-authoritative.
311
267
  *
268
+ * The new Claim Stake's rent settlement authority must not exist and stays
269
+ * guarded absent until signing.
270
+ *
312
271
  * @example
313
272
  * ```ts
314
273
  * declare const ctx: SageContext;
@@ -332,21 +291,11 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
332
291
  if (canonicalBody !== body.address) {
333
292
  fail(`Celestial Body ${body.address} is not the canonical Body ${String(body.id)} for the configured Game. Refresh the Body and retry.`, body.address);
334
293
  }
335
- if (player.playerProfile.address !== options.authorization.profile) {
336
- fail(`Starbase Player ${player.address} does not belong to authorization Profile ${options.authorization.profile}. Refresh the Starbase Player for that Profile and retry.`, player.address);
337
- }
338
- const character = await findCharacterPda(options.authorization.profile, game);
339
- if (player.character.address !== character) {
340
- fail(`Starbase Player ${player.address} does not reference the canonical Character ${character} for authorization Profile ${options.authorization.profile}. Refresh the Starbase Player and retry.`, player.address);
341
- }
342
- const canonicalPlayer = await deriveStarbasePlayerAddress(player.system.address, character);
343
- if (canonicalPlayer !== player.address) {
344
- fail(`Starbase Player ${player.address} is not canonical for its Character and System. Refresh the Starbase Player and retry.`, player.address);
345
- }
294
+ const character = await requireProfileStarbasePlayer(player, options.authorization.profile, game);
346
295
  if (body.system.address !== player.system.address) {
347
296
  fail(`Celestial Body ${body.address} belongs to System ${body.system.address}, but Starbase Player ${player.address} belongs to System ${player.system.address}. Choose both snapshots from the same System and retry.`, body.address);
348
297
  }
349
- const [freshGame, currentAccounts] = await Promise.all([
298
+ const [freshGame, currentAccounts, rent] = await Promise.all([
350
299
  readFreshGameClaimStakeConfiguration(ctx),
351
300
  readContextRawAccounts(ctx, [
352
301
  body.address,
@@ -355,6 +304,7 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
355
304
  body.system.address,
356
305
  options.claimStake,
357
306
  ]),
307
+ requireNewStakeRent(ctx, options.claimStake),
358
308
  ]);
359
309
  if (freshGame === undefined) {
360
310
  fail(`Configured Game ${game} is missing. Refresh the Game catalog and retry placement.`, game);
@@ -385,11 +335,10 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
385
335
  }
386
336
  const currentBodyAccount = requireAccount(bodyAccount, body.address, `Celestial Body ${body.address} is missing. Refresh the Body and retry placement.`);
387
337
  const currentPlayerAccount = requireAccount(playerAccount, player.address, `Starbase Player ${player.address} is missing. Refresh it and retry placement.`);
388
- const currentCharacterAccount = requireAccount(characterAccount, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry placement.`);
389
338
  const currentSystemAccount = requireAccount(systemAccount, body.system.address, `Star System ${body.system.address} is missing. Refresh the System and retry placement.`);
390
339
  const currentBody = celestialBodyAccountDefinition.decode(currentBodyAccount);
391
340
  const currentPlayer = starbasePlayerAccountDefinition.decode(currentPlayerAccount);
392
- const currentCharacter = characterAccountDefinition.decode(currentCharacterAccount);
341
+ const { account: currentCharacterAccount } = requireProfileCharacter(characterAccount, character, options.authorization.profile, game, 'placement');
393
342
  const currentSystem = starSystemAccountDefinition.decode(currentSystemAccount);
394
343
  const canonicalSystem = await deriveStarSystemAddress(game, currentSystem.systemId);
395
344
  if (canonicalSystem !== body.system.address ||
@@ -411,10 +360,6 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
411
360
  currentPlayer.totalCrew - currentPlayer.busyCrew !== player.availableCrew) {
412
361
  fail(`Starbase Player ${player.address} changed or does not match the authorization Profile, canonical Character, System, or available crew. Refresh it and retry.`, player.address);
413
362
  }
414
- if (currentCharacter.playerProfile !== options.authorization.profile ||
415
- currentCharacter.game !== game) {
416
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry.`, character);
417
- }
418
363
  const currentCargo = currentPlayer.cargo.amounts.find(({ cargoId }) => cargoId === definition.cargoId)?.amount ?? 0n;
419
364
  if (currentCargo !== cargoQuantity || currentCargo < 1n) {
420
365
  fail(`Starbase Player ${player.address} Claim Stake cargo changed from the loaded snapshot or is no longer sufficient. Refresh its cargo and retry.`, player.address);
@@ -426,25 +371,26 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
426
371
  }
427
372
  const level = LEVEL_INDEX[currentSystem.starbase.level];
428
373
  const tier = TIER_INDEX[definition.tier];
429
- const plotLimit = currentDetails.claimStakePlots[level][tier];
430
- const usedPlots = claimedCount(currentDetails, definition.id);
431
- if (usedPlots >= plotLimit) {
432
- fail(`Planet ${body.name} (${body.address}) has no available ${definition.name} plot at Starbase ${currentSystem.starbase.level}; ${String(usedPlots)} of ${String(plotLimit)} known plots are occupied. Choose another eligible Planet or tier, refresh it, and retry.`, body.address);
374
+ // The program places the stake in the first plot row up to the Starbase's
375
+ // level with room for its tier; stakes of every tier share each row.
376
+ if (claimStakePlacementRow(currentDetails.claimStakePlots, currentDetails.claimedPlots, tier, level) === undefined) {
377
+ fail(`Planet ${body.name} (${body.address}) has no free tier-${definition.tier} Claim Stake plot at Starbase ${currentSystem.starbase.level} or any lower level; every reachable plot row is full. Choose another eligible Planet or tier, refresh it, and retry.`, body.address);
433
378
  }
434
379
  const profileFaction = await deriveProfileFactionAddress(options.authorization.profile);
435
- const currencyCache = await deriveCurrencyCacheAddress(game);
380
+ const currencyCache = await deriveCurrencyCache(game).then(([address]) => address);
436
381
  const description = `Place ${definition.name} on Planet ${body.name} (${body.address}) with bundled hub ${hub.name}, ${String(hub.neededCrew)} crew, and ${String(options.initialRentAmount)} raw initial rent.`;
437
- return createPlan({
382
+ return buildPlannerPlan({
438
383
  kind: 'claim-stakes.place',
439
384
  preconditions: [
440
385
  ...(await Promise.all([
441
- precondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(definition.id)} and bundled hub ${String(hub.id)} eligibility.`),
442
- precondition(currentBodyAccount, `Planet ${body.name} retains an available ${definition.name} plot and its configured Game/System relationship.`),
443
- precondition(currentPlayerAccount, `Starbase Player ${player.address} retains its Profile, Character, System, available crew, and ${cargoName} balance.`),
444
- precondition(currentCharacterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
445
- precondition(currentSystemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and active Starbase level.`),
386
+ accountStatePrecondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(definition.id)} and bundled hub ${String(hub.id)} eligibility.`),
387
+ accountStatePrecondition(currentBodyAccount, `Planet ${body.name} retains an available ${definition.name} plot and its configured Game/System relationship.`),
388
+ accountStatePrecondition(currentPlayerAccount, `Starbase Player ${player.address} retains its Profile, Character, System, available crew, and ${cargoName} balance.`),
389
+ characterPrecondition(currentCharacterAccount, options.authorization.profile),
390
+ accountStatePrecondition(currentSystemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and active Starbase level.`),
446
391
  ])),
447
- absentPrecondition(options.claimStake, 'The caller-supplied Claim Stake signer address remains unallocated.'),
392
+ accountAbsentPrecondition(options.claimStake, 'The caller-supplied Claim Stake signer address remains unallocated.'),
393
+ ...rent.guards,
448
394
  ],
449
395
  steps: [
450
396
  {
@@ -460,6 +406,7 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
460
406
  hubBuildingId: hub.id,
461
407
  initialRentAmount: options.initialRentAmount,
462
408
  profileFaction,
409
+ rentAuthority: rent.rentAuthority,
463
410
  systemAndStarbasePlayerStarbasePlayer: player.address,
464
411
  systemAndStarbasePlayerSystem: body.system.address,
465
412
  }),
@@ -481,9 +428,10 @@ function sameQuantities(left, right) {
481
428
  [...leftById].every(([buildingId, quantity]) => rightById.get(buildingId) === quantity));
482
429
  }
483
430
  function sameStakeState(left, right) {
484
- if (left.kind === 'deactivated')
485
- return false;
486
431
  switch (left.kind) {
432
+ case 'deactivated':
433
+ return (right.kind === 'deactivated' &&
434
+ left.deactivatedAtUnixSeconds === right.deactivatedAtUnixSeconds);
487
435
  case 'active':
488
436
  return right.kind === 'active';
489
437
  case 'design':
@@ -492,12 +440,173 @@ function sameStakeState(left, right) {
492
440
  sameQuantities(left.buildings, right.buildings));
493
441
  }
494
442
  }
495
- const LEGACY_CONTROLLER_ID = Object.freeze({
496
- unaligned: 0,
497
- mud: 1,
498
- oni: 2,
499
- ustur: 3,
500
- });
443
+ /** Finalization and deconstruction release crew the stake committed, which
444
+ * only a version-1 instance records; the program refuses a version-0 instance,
445
+ * whose stored crew is an unverified nominal value. */
446
+ function requireCommittedCrewVersion(current, stake, outcome) {
447
+ if (current.version !== 1) {
448
+ fail(`Claim Stake ${stake.address} is a version ${String(current.version)} instance, which records no committed crew, so it cannot be ${outcome}. Only version-1 Claim Stakes support this action; choose another Claim Stake.`, stake.address);
449
+ }
450
+ }
451
+ // Common facts for planners acting on an owned, placed Claim Stake. Each
452
+ // planner keeps its own eligibility checks and Plan wording at its call site;
453
+ // `action` completes the shared remedies ("retry <action>").
454
+ /** Requires the loaded Claim Stake to belong to the configured Game and to the
455
+ * authorization Profile's canonical Character, which it returns. */
456
+ async function requireOwnedStake(stake, game, profile) {
457
+ if (stake.game !== game) {
458
+ fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
459
+ }
460
+ const character = await findCharacterPda(profile, game);
461
+ if (stake.ownerCharacter.address !== character) {
462
+ fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
463
+ }
464
+ return character;
465
+ }
466
+ /** Reads and decodes the fresh Claim Stake and Body, with `extra` accounts in
467
+ * the same batch. */
468
+ async function readOwnedStake(ctx, stake, action, extra = []) {
469
+ const [stakeCandidate, bodyCandidate, ...extraAccounts] = await readContextRawAccounts(ctx, [
470
+ stake.address,
471
+ stake.body.address,
472
+ ...extra,
473
+ ]);
474
+ const stakeAccount = requireAccount(stakeCandidate, stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry ${action}.`);
475
+ const bodyAccount = requireAccount(bodyCandidate, stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry ${action}.`);
476
+ return {
477
+ bodyAccount,
478
+ currentBody: celestialBodyAccountDefinition.decode(bodyAccount),
479
+ currentStake: claimStakeAccountDefinition.decode(stakeAccount),
480
+ extraAccounts,
481
+ stakeAccount,
482
+ };
483
+ }
484
+ /** Whether the fresh Claim Stake keeps the loaded ownership, Body,
485
+ * definitions, buildings and System generation. */
486
+ function sameOwnedStake(current, stake, game, character) {
487
+ return (current.game === game &&
488
+ current.body === stake.body.address &&
489
+ current.ownerCharacter === character &&
490
+ current.claimStakeDefinitionId === stake.claimStakeDefinitionId &&
491
+ current.claimStakeDefinitionSeqId === stake.claimStakeDefinitionSeqId &&
492
+ current.buildingDefinitionSeqId === stake.buildingDefinitionSeqId &&
493
+ current.systemSeqId === stake.systemSeqId &&
494
+ sameQuantities(current.buildings, stake.buildings));
495
+ }
496
+ /** Whether the fresh Claim Stake keeps the loaded transfer facts: ownership,
497
+ * Body, active state, System generation, resources and capacity. */
498
+ function sameTransferStake(current, stake, game, character) {
499
+ return (current.game === game &&
500
+ current.ownerCharacter === character &&
501
+ current.body === stake.body.address &&
502
+ current.systemSeqId === stake.systemSeqId &&
503
+ current.state.kind === 'active' &&
504
+ current.production.cargoPod.storageCost ===
505
+ stake.resources.capacity.usedRaw &&
506
+ current.production.resourceCapacityRaw ===
507
+ stake.resources.capacity.totalRaw &&
508
+ sameCargoAmounts(current.production.cargoPod.amounts, stake.resources.inventory.items.map(({ id, quantityRaw }) => ({
509
+ amount: quantityRaw,
510
+ id,
511
+ }))));
512
+ }
513
+ /** Whether the fresh Fleet keeps the loaded Game, owner, name, cargo and
514
+ * capacities. */
515
+ function sameTransferFleet(current, fleet, game, profile) {
516
+ return (current.game === game &&
517
+ current.ownerProfile === profile &&
518
+ current.name === fleet.name &&
519
+ current.ammoBank.cargoId === fleet.ammo.id &&
520
+ current.ammoBank.amount === fleet.ammo.amount &&
521
+ current.fuelTank.cargoId === fleet.fuel.id &&
522
+ current.fuelTank.amount === fleet.fuel.amount &&
523
+ current.cargoHold.storageCost === fleet.cargoHold.storageCost &&
524
+ sameCargoAmounts(current.cargoHold.amounts, fleet.cargoHold.items) &&
525
+ current.stats.cargo.ammoCapacity === fleet.capacities.ammo.total &&
526
+ current.stats.cargo.fuelCapacity === fleet.capacities.fuel.total &&
527
+ current.stats.cargo.cargoCapacity === fleet.capacities.cargo.total);
528
+ }
529
+ /** Requires the fresh Body to be the Claim Stake's canonical Planet in the
530
+ * configured Game at the Claim Stake's System generation. */
531
+ async function requireStakePlanet(currentBody, stake, game, action) {
532
+ if (currentBody.kind !== 'planet' ||
533
+ currentBody.game !== game ||
534
+ currentBody.systemSequenceId !== stake.systemSeqId ||
535
+ (await deriveCelestialBodyAddress(game, currentBody.id)) !==
536
+ stake.body.address) {
537
+ fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying ${action}.`, stake.body.address);
538
+ }
539
+ return currentBody;
540
+ }
541
+ /** Requires and decodes the canonical Character of the authorization Profile in
542
+ * the configured Game. */
543
+ function requireProfileCharacter(candidate, character, profile, game, action) {
544
+ const account = requireAccount(candidate, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry ${action}.`);
545
+ const current = characterAccountDefinition.decode(account);
546
+ if (current.playerProfile !== profile || current.game !== game) {
547
+ fail(`Character ${character} does not match authorization Profile ${profile} and configured Game ${game}. Refresh the Character and retry ${action}.`, character);
548
+ }
549
+ return { account, current };
550
+ }
551
+ /** Reads the Claim Stake's System and the authorization Profile's canonical
552
+ * Starbase Player there, with `extra` accounts in the same batch. Requires a
553
+ * canonical, current System with an active Starbase that lists the Body, and a
554
+ * Starbase Player of the Profile, Character and System. */
555
+ async function readStakeStarbase(ctx, stake, game, system, character, profile, action, extra = []) {
556
+ const player = await deriveStarbasePlayerAddress(system, character);
557
+ const [systemCandidate, playerCandidate, ...extraAccounts] = await readContextRawAccounts(ctx, [system, player, ...extra]);
558
+ const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry ${action}.`);
559
+ const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${profile}. Register or refresh it in the Claim Stake's System before retrying ${action}.`);
560
+ const currentSystem = starSystemAccountDefinition.decode(systemAccount);
561
+ const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
562
+ if (currentSystem.game !== game ||
563
+ currentSystem.sequenceId !== stake.systemSeqId ||
564
+ currentSystem.starbase === undefined ||
565
+ !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
566
+ (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
567
+ fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, active nested Starbase, or Claim Stake Body relationship. Refresh the System and retry ${action}.`, system);
568
+ }
569
+ if (currentPlayer.playerProfile !== profile ||
570
+ currentPlayer.character !== character ||
571
+ currentPlayer.system !== system) {
572
+ fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry ${action}.`, player);
573
+ }
574
+ return {
575
+ currentPlayer,
576
+ currentSystem,
577
+ extraAccounts,
578
+ player,
579
+ playerAccount,
580
+ starbase: currentSystem.starbase,
581
+ systemAccount,
582
+ };
583
+ }
584
+ async function claimStakeCurrencyCache(game) {
585
+ const [currencyCache] = await deriveCurrencyCache(game);
586
+ return currencyCache;
587
+ }
588
+ /** Requires the canonical CurrencyCache and returns its Plan guard. */
589
+ async function requireCurrencyCache(candidate, currencyCache, game, action, planName) {
590
+ await validateCurrencyCache(requireAccount(candidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry ${action}.`), game, planName);
591
+ return {
592
+ kind: 'currency-cache',
593
+ address: currencyCache,
594
+ game,
595
+ describes: 'The canonical CurrencyCache remains assigned to the configured Game; shared accounting may change.',
596
+ };
597
+ }
598
+ function planetPrecondition(account, system) {
599
+ return accountStatePrecondition(account, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`);
600
+ }
601
+ function characterPrecondition(account, profile) {
602
+ return accountStatePrecondition(account, `The canonical Character remains assigned to Profile ${profile} in the configured Game.`);
603
+ }
604
+ function systemPrecondition(account, name) {
605
+ return accountStatePrecondition(account, `Star System ${name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`);
606
+ }
607
+ function playerPrecondition(account, player, facts = 'Profile, Character, and System') {
608
+ return accountStatePrecondition(account, `Starbase Player ${player} retains its ${facts}.`);
609
+ }
501
610
  function buildingDescription(change, name) {
502
611
  return `${change.kind} ${String(change.quantity)} ${name}`;
503
612
  }
@@ -506,8 +615,17 @@ function buildingDescription(change, name) {
506
615
  * The planner verifies the fresh Claim Stake, definition sequences, canonical
507
616
  * Profile/Character/Body/System/Starbase Player graph, optional
508
617
  * FactionOwnership sidecar, building quantities, slots, crew, and known
509
- * construction cargo. Rent synchronization and the final concurrent sequence
510
- * race remain chain-authoritative.
618
+ * construction cargo. Construction cargo is debited from the Starbase Player's
619
+ * Starbase cargo at finalization, never from the Claim Stake's own storage, so
620
+ * the planner checks the fresh Starbase Player pod. Rent synchronization and
621
+ * the final concurrent sequence race remain chain-authoritative.
622
+ *
623
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
624
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
625
+ *
626
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
627
+ * settlement authority must not exist, and both stay guarded absent until
628
+ * signing. The instruction carries no rent history.
511
629
  *
512
630
  * @example
513
631
  * ```ts
@@ -521,88 +639,41 @@ function buildingDescription(change, name) {
521
639
  export async function planClaimStakeBuildingChanges(ctx, stake, input) {
522
640
  const options = validateBuildingChangeOptions(input);
523
641
  const game = requireGameAddress(ctx);
642
+ const { profile } = options.authorization;
524
643
  if (stake.state.kind === 'deactivated') {
525
644
  fail(`Claim Stake ${stake.address} is deactivated; building changes require an active or design Claim Stake. Refresh it or choose another owned Claim Stake before retrying.`, stake.address);
526
645
  }
527
- if (stake.game !== game) {
528
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
529
- }
530
- const character = await findCharacterPda(options.authorization.profile, game);
531
- if (stake.ownerCharacter.address !== character) {
532
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
533
- }
534
- const [freshGame, firstAccounts] = await Promise.all([
646
+ const character = await requireOwnedStake(stake, game, profile);
647
+ const action = 'building changes';
648
+ const [freshGame, loaded, rent] = await Promise.all([
535
649
  readFreshGameClaimStakeConfiguration(ctx),
536
- readContextRawAccounts(ctx, [stake.address, stake.body.address, character]),
650
+ readOwnedStake(ctx, stake, action, [character]),
651
+ requireDisabledStakeRent(ctx, game, stake.address, action),
537
652
  ]);
538
653
  if (freshGame === undefined) {
539
654
  fail(`Configured Game ${game} is missing. Refresh the Game catalog and retry building changes.`, game);
540
655
  }
541
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry building changes.`);
542
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry building changes.`);
543
- const characterAccount = requireAccount(firstAccounts[2], character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry building changes.`);
544
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
545
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
546
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
547
- if (currentStake.game !== game ||
548
- currentStake.body !== stake.body.address ||
549
- currentStake.ownerCharacter !== character ||
550
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
551
- currentStake.claimStakeDefinitionSeqId !==
552
- stake.claimStakeDefinitionSeqId ||
553
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
554
- currentStake.systemSeqId !== stake.systemSeqId ||
555
- !sameQuantities(currentStake.buildings, stake.buildings) ||
656
+ const { bodyAccount, currentStake, stakeAccount } = loaded;
657
+ if (!sameOwnedStake(currentStake, stake, game, character) ||
556
658
  currentStake.neededCrew !== stake.neededCrew) {
557
659
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, crew, or System generation. Refresh it and retry building changes.`, stake.address);
558
660
  }
559
- if (currentCharacter.playerProfile !== options.authorization.profile ||
560
- currentCharacter.game !== game) {
561
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry building changes.`, character);
562
- }
563
- if (currentBody.kind !== 'planet' ||
564
- currentBody.game !== game ||
565
- currentBody.systemSequenceId !== stake.systemSeqId ||
566
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
567
- stake.body.address) {
568
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying.`, stake.body.address);
569
- }
570
- const system = currentBody.system;
571
- const [player, ownership, profileFaction] = await Promise.all([
572
- deriveStarbasePlayerAddress(system, character),
661
+ const { system } = await requireStakePlanet(loaded.currentBody, stake, game, action);
662
+ const { account: characterAccount } = requireProfileCharacter(loaded.extraAccounts[0], character, profile, game, action);
663
+ const [ownership, profileFaction] = await Promise.all([
573
664
  deriveFactionOwnershipAddress(game, 'starbase', system),
574
- deriveProfileFactionAddress(options.authorization.profile),
665
+ deriveProfileFactionAddress(profile),
575
666
  ]);
576
- const [systemAccountCandidate, playerAccountCandidate, ownershipAccount, profileFactionAccountCandidate,] = await readContextRawAccounts(ctx, [
577
- system,
578
- player,
579
- ownership,
580
- profileFaction,
581
- ]);
582
- const systemAccount = requireAccount(systemAccountCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry building changes.`);
583
- const playerAccount = requireAccount(playerAccountCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying.`);
584
- const profileFactionAccount = requireAccount(profileFactionAccountCandidate, profileFaction, `ProfileFaction ${profileFaction} is missing for authorization Profile ${options.authorization.profile}. Choose or repair the Profile faction, refresh it, and retry building changes.`);
585
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
586
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
667
+ const { currentPlayer, currentSystem, extraAccounts: [ownershipAccount, profileFactionCandidate], player, playerAccount, starbase, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [ownership, profileFaction]);
668
+ const profileFactionAccount = requireAccount(profileFactionCandidate, profileFaction, `ProfileFaction ${profileFaction} is missing for authorization Profile ${profile}. Choose or repair the Profile faction, refresh it, and retry building changes.`);
587
669
  const currentProfileFaction = profileFactionIdentityAccountDefinition.decode(profileFactionAccount);
588
- if (currentSystem.game !== game ||
589
- currentSystem.sequenceId !== stake.systemSeqId ||
590
- currentSystem.starbase === undefined ||
591
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
592
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry building changes.`, system);
593
- }
594
- if (currentPlayer.playerProfile !== options.authorization.profile ||
595
- currentPlayer.character !== character ||
596
- currentPlayer.system !== system) {
597
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry building changes.`, player);
670
+ if (currentProfileFaction.profile !== profile) {
671
+ fail(`ProfileFaction ${profileFaction} does not match authorization Profile ${profile}. Refresh or repair the Profile faction before retrying building changes.`, profileFaction);
598
672
  }
599
- if (currentProfileFaction.profile !== options.authorization.profile) {
600
- fail(`ProfileFaction ${profileFaction} does not match authorization Profile ${options.authorization.profile}. Refresh or repair the Profile faction before retrying building changes.`, profileFaction);
601
- }
602
- let controllerFactionId = LEGACY_CONTROLLER_ID[currentSystem.starbase.owner];
673
+ let controllerFactionId = factionValue(starbase.owner);
603
674
  let ownershipPrecondition;
604
675
  if (ownershipAccount === undefined) {
605
- ownershipPrecondition = absentPrecondition(ownership, `The canonical Starbase FactionOwnership ${ownership} remains unallocated so the chain applies its legacy controller gate.`);
676
+ ownershipPrecondition = accountAbsentPrecondition(ownership, `The canonical Starbase FactionOwnership ${ownership} remains unallocated so the chain applies its legacy controller gate.`);
606
677
  }
607
678
  else {
608
679
  const currentOwnership = factionOwnershipAccountDefinition.decode(ownershipAccount);
@@ -614,7 +685,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
614
685
  }
615
686
  controllerFactionId =
616
687
  currentOwnership.controllingFactionId ?? controllerFactionId;
617
- ownershipPrecondition = await precondition(ownershipAccount, `The Starbase FactionOwnership remains assigned to System ${system} at its current generation.`);
688
+ ownershipPrecondition = await accountStatePrecondition(ownershipAccount, `The Starbase FactionOwnership remains assigned to System ${system} at its current generation.`);
618
689
  }
619
690
  if (currentProfileFaction.factionId !== controllerFactionId) {
620
691
  fail(`ProfileFaction ${profileFaction} faction ${String(currentProfileFaction.factionId)} does not match the current Starbase controller faction ${String(controllerFactionId)}. Use a Profile aligned with the current controller, refresh the faction relationship, and retry building changes.`, profileFaction);
@@ -714,10 +785,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
714
785
  if (addedCrew > availableCrew) {
715
786
  fail(`Building additions require ${String(addedCrew)} crew, but Starbase Player ${player} has only ${String(availableCrew)} known crew capacity available. Free crew, refresh the Starbase Player, and retry.`, player);
716
787
  }
717
- const cargo = new Map(currentStake.production.cargoPod.amounts.map(({ cargoId, amount }) => [
718
- cargoId,
719
- amount,
720
- ]));
788
+ const cargo = new Map(currentPlayer.cargo.amounts.map(({ cargoId, amount }) => [cargoId, amount]));
721
789
  const constructionCosts = new Map();
722
790
  for (const change of options.buildingChanges) {
723
791
  if (change.kind !== 'add')
@@ -732,7 +800,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
732
800
  const available = cargo.get(cargoId) ?? 0n;
733
801
  if (required > available) {
734
802
  const snapshotItem = stake.resources.inventory.items.find(({ id }) => id === cargoId);
735
- fail(`Building construction requires ${String(required)} raw ${snapshotItem?.name ?? `cargo ${String(cargoId)}`}, but Claim Stake ${stake.address} has only ${String(available)} known. Add construction cargo, refresh the Claim Stake, and retry.`, stake.address);
803
+ fail(`Building construction requires ${String(required)} raw ${snapshotItem?.name ?? `cargo ${String(cargoId)}`}, but Starbase Player ${player} has only ${String(available)} known in Starbase cargo. Construction cargo is taken from the Starbase, not from the Claim Stake. Deposit it at the Starbase, refresh the Starbase Player, and retry.`, player);
736
804
  }
737
805
  }
738
806
  if (currentStake.production.resourceCapacityRaw !==
@@ -744,26 +812,30 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
744
812
  if (currentStake.production.cargoPod.storageCost > resourceStorageRaw) {
745
813
  fail(`The resulting Claim Stake design provides ${String(resourceStorageRaw)} raw storage capacity, below the fresh known cargo usage of ${String(currentStake.production.cargoPod.storageCost)}. Keep enough storage buildings, remove cargo, refresh the Claim Stake, and retry building changes.`, stake.address);
746
814
  }
747
- const currencyCache = await deriveCurrencyCacheAddress(game);
815
+ const currencyCache = await claimStakeCurrencyCache(game);
748
816
  const description = `Change buildings on Claim Stake ${stake.address}: ${descriptions.join('; ')}.`;
749
- return createPlan({
817
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address);
818
+ return buildPlannerPlan({
750
819
  kind: 'claim-stakes.building-changes',
751
820
  preconditions: [
752
821
  ...(await Promise.all([
753
- precondition(freshGame.account, 'The configured Game retains the exact Claim Stake and building definitions.'),
754
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, cargo, crew, and Body relationship.`),
755
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
756
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
757
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and nested Starbase.`),
758
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, System, and available crew.`),
759
- precondition(profileFactionAccount, `ProfileFaction ${profileFaction} remains assigned to Profile ${options.authorization.profile} and aligned with the current Starbase controller.`),
822
+ accountStatePrecondition(freshGame.account, 'The configured Game retains the exact Claim Stake and building definitions.'),
823
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, cargo, crew, and Body relationship.`),
824
+ planetPrecondition(bodyAccount, system),
825
+ characterPrecondition(characterAccount, profile),
826
+ systemPrecondition(systemAccount, currentSystem.name),
827
+ playerPrecondition(playerAccount, player, 'Profile, Character, System, and available crew'),
828
+ accountStatePrecondition(profileFactionAccount, `ProfileFaction ${profileFaction} remains assigned to Profile ${profile} and aligned with the current Starbase controller.`),
760
829
  ])),
761
830
  ownershipPrecondition,
831
+ xp.guard,
832
+ ...rent.guards,
762
833
  ],
763
834
  steps: [
764
835
  {
765
836
  describes: description,
766
837
  instruction: adaptClaimStakeBuildingChangesInstruction({
838
+ ...xp.accounts,
767
839
  authorization: options.authorization,
768
840
  buildingChanges: options.buildingChanges.map(({ buildingId, kind, quantity }) => [buildingId, kind === 'add' ? quantity : -quantity]),
769
841
  celestialBody: stake.body.address,
@@ -772,6 +844,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
772
844
  factionOwnership: ownership,
773
845
  game,
774
846
  profileFaction,
847
+ ...rent.accounts,
775
848
  sequenceId,
776
849
  starbasePlayer: player,
777
850
  system,
@@ -789,6 +862,14 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
789
862
  * CurrencyCache, and a known positive rent balance. Rent timing and the final
790
863
  * crew/building validation remain chain-authoritative.
791
864
  *
865
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
866
+ * The Character XP budget and configuration must also be reconciled.
867
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
868
+ *
869
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
870
+ * settlement authority must not exist, and both stay guarded absent until
871
+ * signing. The instruction carries no rent history.
872
+ *
792
873
  * @example
793
874
  * ```ts
794
875
  * declare const ctx: SageContext;
@@ -804,22 +885,19 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
804
885
  export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
805
886
  const options = validateFinalizeBuildingChangeOptions(input);
806
887
  const game = requireGameAddress(ctx);
888
+ const { profile } = options.authorization;
807
889
  if (stake.state.kind !== 'design') {
808
890
  fail(`Claim Stake ${stake.address} is not in design state. Refresh it and finalize only a pending building design.`, stake.address);
809
891
  }
810
- if (stake.game !== game) {
811
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
812
- }
813
- const character = await findCharacterPda(options.authorization.profile, game);
814
- if (stake.ownerCharacter.address !== character) {
815
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
816
- }
892
+ const character = await requireOwnedStake(stake, game, profile);
817
893
  if (stake.rentBalanceRaw <= 0n) {
818
894
  fail(`Claim Stake ${stake.address} has a known delinquent rent balance of ${String(stake.rentBalanceRaw)} raw units. Top up rent, refresh the Claim Stake, and retry finalization.`, stake.address);
819
895
  }
820
- const [freshGame, firstAccounts] = await Promise.all([
896
+ const action = 'finalization';
897
+ const [freshGame, loaded, rent] = await Promise.all([
821
898
  readFreshGameClaimStakeConfiguration(ctx),
822
- readContextRawAccounts(ctx, [stake.address, stake.body.address, character]),
899
+ readOwnedStake(ctx, stake, action, [character]),
900
+ requireDisabledStakeRent(ctx, game, stake.address, action),
823
901
  ]);
824
902
  if (freshGame === undefined) {
825
903
  fail(`Configured Game ${game} is missing. Refresh the Game catalog and retry finalization.`, game);
@@ -828,81 +906,49 @@ export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
828
906
  freshGame.data.buildings.sequenceId !== stake.buildingDefinitionSeqId) {
829
907
  fail(`Claim Stake ${stake.address} definition sequence is stale against the current Game catalog. Refresh the Claim Stake and definitions to rule out stale local data. If the on-chain generation still differs, use a Claim Stake matching the current catalog before retrying finalization.`, stake.address);
830
908
  }
831
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry finalization.`);
832
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry finalization.`);
833
- const characterAccount = requireAccount(firstAccounts[2], character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry finalization.`);
834
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
835
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
836
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
837
- if (currentStake.game !== game ||
838
- currentStake.body !== stake.body.address ||
839
- currentStake.ownerCharacter !== character ||
840
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
841
- currentStake.claimStakeDefinitionSeqId !==
842
- stake.claimStakeDefinitionSeqId ||
843
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
844
- currentStake.systemSeqId !== stake.systemSeqId ||
909
+ const { bodyAccount, currentStake, stakeAccount } = loaded;
910
+ if (!sameOwnedStake(currentStake, stake, game, character) ||
845
911
  currentStake.rentBalanceRaw !== stake.rentBalanceRaw ||
846
- !sameQuantities(currentStake.buildings, stake.buildings) ||
847
912
  currentStake.state.kind !== 'design' ||
848
913
  currentStake.state.sequenceId !== stake.state.sequenceId ||
849
914
  !sameQuantities(currentStake.state.buildings, stake.state.buildings)) {
850
915
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, design sequence, rent, or System generation. Refresh it and retry finalization.`, stake.address);
851
916
  }
852
- if (currentCharacter.playerProfile !== options.authorization.profile ||
853
- currentCharacter.game !== game) {
854
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry finalization.`, character);
855
- }
856
- if (currentBody.kind !== 'planet' ||
857
- currentBody.game !== game ||
858
- currentBody.systemSequenceId !== stake.systemSeqId ||
859
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
860
- stake.body.address) {
861
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying finalization.`, stake.body.address);
862
- }
863
- const system = currentBody.system;
864
- const player = await deriveStarbasePlayerAddress(system, character);
865
- const currencyCache = await deriveCurrencyCacheAddress(game);
866
- const [systemCandidate, playerCandidate, currencyCandidate] = await readContextRawAccounts(ctx, [system, player, currencyCache]);
867
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry finalization.`);
868
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying finalization.`);
869
- const currencyAccount = requireAccount(currencyCandidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry finalization.`);
870
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
871
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
872
- if (currentSystem.game !== game ||
873
- currentSystem.sequenceId !== stake.systemSeqId ||
874
- currentSystem.starbase === undefined ||
875
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
876
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry finalization.`, system);
877
- }
878
- if (currentPlayer.playerProfile !== options.authorization.profile ||
879
- currentPlayer.character !== character ||
880
- currentPlayer.system !== system) {
881
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry finalization.`, player);
882
- }
883
- validateCurrencyCache(currencyAccount, game, currencyCache, 'finalization');
917
+ requireCommittedCrewVersion(currentStake, stake, 'finalized');
918
+ const { system } = await requireStakePlanet(loaded.currentBody, stake, game, action);
919
+ const { account: characterAccount } = requireProfileCharacter(loaded.extraAccounts[0], character, profile, game, action);
920
+ const currencyCache = await claimStakeCurrencyCache(game);
921
+ const { currentSystem, extraAccounts: [currencyCandidate], player, playerAccount, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [currencyCache]);
922
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake finalization');
884
923
  const description = `Finalize building changes on Claim Stake ${stake.address}.`;
885
- return createPlan({
924
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address, character);
925
+ return buildPlannerPlan({
886
926
  kind: 'claim-stakes.finalize-building-changes',
887
- preconditions: await Promise.all([
888
- precondition(freshGame.account, 'The configured Game remains current.'),
889
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, rent balance, Body, and System generation.`),
890
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
891
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
892
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and nested Starbase.`),
893
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
894
- precondition(currencyAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
895
- ]),
927
+ preconditions: [
928
+ xp.guard,
929
+ ...(await Promise.all([
930
+ accountStatePrecondition(freshGame.account, 'The configured Game remains current.'),
931
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, rent balance, Body, and System generation.`),
932
+ characterPrecondition(characterAccount, profile),
933
+ planetPrecondition(bodyAccount, system),
934
+ systemPrecondition(systemAccount, currentSystem.name),
935
+ playerPrecondition(playerAccount, player),
936
+ ])),
937
+ currencyGuard,
938
+ ...rent.guards,
939
+ ],
896
940
  steps: [
897
941
  {
898
942
  describes: description,
899
943
  instruction: adaptFinalizeClaimStakeBuildingChangesInstruction({
944
+ ...xp.accounts,
900
945
  authorization: options.authorization,
901
946
  celestialBody: stake.body.address,
902
947
  character,
903
948
  claimStakeInstance: stake.address,
904
949
  currencyCache,
905
950
  game,
951
+ ...rent.accounts,
906
952
  systemAndStarbasePlayerStarbasePlayer: player,
907
953
  systemAndStarbasePlayerSystem: system,
908
954
  }),
@@ -920,6 +966,13 @@ export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
920
966
  * state; rent arithmetic and concurrent state transitions remain
921
967
  * chain-authoritative.
922
968
  *
969
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
970
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
971
+ *
972
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
973
+ * settlement authority must not exist, and both stay guarded absent until
974
+ * signing. The instruction carries no rent history.
975
+ *
923
976
  * @example
924
977
  * ```ts
925
978
  * declare const ctx: SageContext;
@@ -934,85 +987,52 @@ export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
934
987
  export async function planCancelClaimStakeBuildingChanges(ctx, stake, input) {
935
988
  const options = validateCancelBuildingChangeOptions(input);
936
989
  const game = requireGameAddress(ctx);
990
+ const { profile } = options.authorization;
937
991
  if (stake.state.kind !== 'design') {
938
992
  fail(`Claim Stake ${stake.address} is not in design state. Refresh it and cancel only a pending building design.`, stake.address);
939
993
  }
940
- if (stake.game !== game) {
941
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
942
- }
943
- const character = await findCharacterPda(options.authorization.profile, game);
944
- if (stake.ownerCharacter.address !== character) {
945
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
946
- }
947
- const firstAccounts = await readContextRawAccounts(ctx, [
948
- stake.address,
949
- stake.body.address,
994
+ const character = await requireOwnedStake(stake, game, profile);
995
+ const action = 'cancellation';
996
+ const [{ bodyAccount, currentBody, currentStake, stakeAccount }, rent] = await Promise.all([
997
+ readOwnedStake(ctx, stake, action),
998
+ requireDisabledStakeRent(ctx, game, stake.address, action),
950
999
  ]);
951
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry cancellation.`);
952
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry cancellation.`);
953
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
954
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
955
- if (currentStake.game !== game ||
956
- currentStake.body !== stake.body.address ||
957
- currentStake.ownerCharacter !== character ||
958
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
959
- currentStake.claimStakeDefinitionSeqId !==
960
- stake.claimStakeDefinitionSeqId ||
961
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
962
- currentStake.systemSeqId !== stake.systemSeqId ||
963
- !sameQuantities(currentStake.buildings, stake.buildings) ||
1000
+ if (!sameOwnedStake(currentStake, stake, game, character) ||
964
1001
  currentStake.state.kind !== 'design' ||
965
1002
  currentStake.state.sequenceId !== stake.state.sequenceId ||
966
1003
  !sameQuantities(currentStake.state.buildings, stake.state.buildings)) {
967
1004
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, design sequence, or System generation. Refresh it and retry cancellation.`, stake.address);
968
1005
  }
969
- if (currentBody.kind !== 'planet' ||
970
- currentBody.game !== game ||
971
- currentBody.systemSequenceId !== stake.systemSeqId ||
972
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
973
- stake.body.address) {
974
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying cancellation.`, stake.body.address);
975
- }
976
- const system = currentBody.system;
977
- const player = await deriveStarbasePlayerAddress(system, character);
978
- const currencyCache = await deriveCurrencyCacheAddress(game);
979
- const [systemCandidate, playerCandidate, currencyCandidate] = await readContextRawAccounts(ctx, [system, player, currencyCache]);
980
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry cancellation.`);
981
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying cancellation.`);
982
- const currencyAccount = requireAccount(currencyCandidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry cancellation.`);
983
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
984
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
985
- if (currentSystem.game !== game ||
986
- currentSystem.sequenceId !== stake.systemSeqId ||
987
- currentSystem.starbase === undefined ||
988
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
989
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry cancellation.`, system);
990
- }
991
- if (currentPlayer.playerProfile !== options.authorization.profile ||
992
- currentPlayer.character !== character ||
993
- currentPlayer.system !== system) {
994
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry cancellation.`, player);
995
- }
996
- validateCurrencyCache(currencyAccount, game, currencyCache, 'cancellation');
1006
+ const { system } = await requireStakePlanet(currentBody, stake, game, action);
1007
+ const currencyCache = await claimStakeCurrencyCache(game);
1008
+ const { currentSystem, extraAccounts: [currencyCandidate], player, playerAccount, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [currencyCache]);
1009
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake cancellation');
997
1010
  const description = `Cancel the pending building design on Claim Stake ${stake.address} and return it to active state.`;
998
- return createPlan({
1011
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address);
1012
+ return buildPlannerPlan({
999
1013
  kind: 'claim-stakes.cancel-building-changes',
1000
- preconditions: await Promise.all([
1001
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, Body, and System generation.`),
1002
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
1003
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and nested Starbase.`),
1004
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1005
- precondition(currencyAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
1006
- ]),
1014
+ preconditions: [
1015
+ xp.guard,
1016
+ ...(await Promise.all([
1017
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, Body, and System generation.`),
1018
+ planetPrecondition(bodyAccount, system),
1019
+ systemPrecondition(systemAccount, currentSystem.name),
1020
+ playerPrecondition(playerAccount, player),
1021
+ ])),
1022
+ currencyGuard,
1023
+ ...rent.guards,
1024
+ ],
1007
1025
  steps: [
1008
1026
  {
1009
1027
  describes: description,
1010
1028
  instruction: adaptCancelClaimStakeBuildingChangesInstruction({
1029
+ ...xp.accounts,
1011
1030
  authorization: options.authorization,
1012
1031
  celestialBody: stake.body.address,
1013
1032
  claimStakeInstance: stake.address,
1014
1033
  currencyCache,
1015
1034
  game,
1035
+ ...rent.accounts,
1016
1036
  starbasePlayer: player,
1017
1037
  system,
1018
1038
  }),
@@ -1024,13 +1044,19 @@ export async function planCancelClaimStakeBuildingChanges(ctx, stake, input) {
1024
1044
  }
1025
1045
  /** Plans deconstruction of one owned active Claim Stake.
1026
1046
  *
1027
- * The planner derives the current Planet chronology index and exact plot bucket
1028
- * from the Claim Stake definition tier and fresh Body plot matrix. The only
1047
+ * The planner derives the plot level from the fresh Planet plot row that
1048
+ * records the stake, and the index from its position in that row. The only
1029
1049
  * remaining building must be the definition-compatible zero-cost bundled hub,
1030
1050
  * which deconstruction discards.
1031
1051
  * Rent settlement, cargo return, account closure, and concurrent races remain
1032
1052
  * chain-authoritative; the Plan does not estimate a refund.
1033
1053
  *
1054
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1055
+ * settlement authority must not exist, and both stay guarded absent until
1056
+ * signing. The instruction carries no rent history. Deconstruction also
1057
+ * requires the canonical Currency Cache and closes the Claim Stake's XP ledger;
1058
+ * a Claim Stake without a ledger accrued no XP.
1059
+ *
1034
1060
  * @example
1035
1061
  * ```ts
1036
1062
  * declare const ctx: SageContext;
@@ -1043,46 +1069,27 @@ export async function planCancelClaimStakeBuildingChanges(ctx, stake, input) {
1043
1069
  export async function planDeconstructClaimStake(ctx, stake, input) {
1044
1070
  const options = validateDeconstructOptions(input);
1045
1071
  const game = requireGameAddress(ctx);
1072
+ const { profile } = options.authorization;
1046
1073
  if (stake.state.kind !== 'active') {
1047
1074
  fail(`Claim Stake ${stake.address} is not in active state. Refresh it, finalize or cancel any pending design, and retry deconstruction.`, stake.address);
1048
1075
  }
1049
- if (stake.game !== game) {
1050
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
1051
- }
1052
- const character = await findCharacterPda(options.authorization.profile, game);
1053
- if (stake.ownerCharacter.address !== character) {
1054
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
1055
- }
1056
- const [freshGame, firstAccounts] = await Promise.all([
1076
+ const character = await requireOwnedStake(stake, game, profile);
1077
+ const action = 'deconstruction';
1078
+ const [freshGame, { bodyAccount, currentBody, currentStake, stakeAccount }, rent,] = await Promise.all([
1057
1079
  readFreshGameClaimStakeConfiguration(ctx),
1058
- readContextRawAccounts(ctx, [stake.address, stake.body.address]),
1080
+ readOwnedStake(ctx, stake, action),
1081
+ requireDisabledStakeRent(ctx, game, stake.address, action),
1059
1082
  ]);
1060
1083
  if (freshGame === undefined) {
1061
1084
  fail(`Configured Game ${game} is missing. Refresh the Game definitions and retry deconstruction.`, game);
1062
1085
  }
1063
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry deconstruction.`);
1064
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry deconstruction.`);
1065
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1066
- const currentBody = decodeIndexedCelestialBodyAccount(bodyAccount);
1067
- if (currentStake.game !== game ||
1068
- currentStake.body !== stake.body.address ||
1069
- currentStake.ownerCharacter !== character ||
1070
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
1071
- currentStake.claimStakeDefinitionSeqId !==
1072
- stake.claimStakeDefinitionSeqId ||
1073
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
1074
- currentStake.systemSeqId !== stake.systemSeqId ||
1075
- !sameQuantities(currentStake.buildings, stake.buildings) ||
1086
+ if (!sameOwnedStake(currentStake, stake, game, character) ||
1076
1087
  currentStake.state.kind !== 'active') {
1077
1088
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, active state, or System generation. Refresh it and retry deconstruction.`, stake.address);
1078
1089
  }
1079
- if (currentBody.kind !== 'planet' ||
1080
- currentBody.game !== game ||
1081
- currentBody.systemSequenceId !== stake.systemSeqId ||
1082
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
1083
- stake.body.address) {
1084
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying deconstruction.`, stake.body.address);
1085
- }
1090
+ requireCommittedCrewVersion(currentStake, stake, 'deconstructed');
1091
+ const planet = await requireStakePlanet(currentBody, stake, game, action);
1092
+ const { system } = planet;
1086
1093
  if (freshGame.data.claimStakes.sequenceId !== stake.claimStakeDefinitionSeqId) {
1087
1094
  fail(`Claim Stake ${stake.address} definition generation is stale against the current Game catalog. Refresh the Claim Stake and definitions to rule out stale local data. If the on-chain generation still differs, use a Claim Stake matching the current catalog before retrying deconstruction.`, stake.address);
1088
1095
  }
@@ -1104,92 +1111,84 @@ export async function planDeconstructClaimStake(ctx, stake, input) {
1104
1111
  bundledHub.hubValue !== claimDefinition.hubValue ||
1105
1112
  bundledHub.minimumTier !== claimDefinition.tier ||
1106
1113
  bundledHub.constructionCost.length !== 0) {
1114
+ // Only a building with a construction cost is player-funded; a zero-cost
1115
+ // hub from another definition family is a foreign bundled hub.
1107
1116
  const hasPlayerFundedBuilding = stake.buildings.some((building) => {
1108
1117
  const definition = definitions.get(building.buildingId);
1109
- return (definition === undefined ||
1110
- definition.hubValue !== claimDefinition.hubValue ||
1111
- definition.minimumTier !== claimDefinition.tier ||
1112
- definition.constructionCost.length !== 0);
1118
+ return (definition !== undefined && definition.constructionCost.length !== 0);
1113
1119
  });
1114
1120
  fail(hasPlayerFundedBuilding
1115
1121
  ? `Claim Stake ${stake.address} still contains a player-funded building. Remove and finalize every player-funded building so only its verified bundled hub remains before retrying deconstruction.`
1116
- : `Claim Stake ${stake.address} does not contain exactly one verified bundled hub. Refresh it and restore the definition-compatible bundled hub before retrying deconstruction.`, stake.address);
1117
- }
1118
- const groups = currentBody.details.claimedPlots.filter(({ key }) => key === claimDefinition.id);
1119
- if (groups.length !== 1) {
1120
- fail(`Claim Stake definition ${String(claimDefinition.id)} does not have exactly one coherent group in Planet ${stake.body.address}'s Claim Stake Body data. Refresh the Body before retrying deconstruction.`, stake.body.address);
1121
- }
1122
- const group = groups[0];
1123
- const chronologyMatches = group.chronoAddresses
1124
- .map((address, index) => ({ address, index }))
1125
- .filter(({ address }) => address === stake.address);
1126
- if (chronologyMatches.length !== 1) {
1127
- fail(`Claim Stake ${stake.address} must appear exactly once in the Planet chronology before its deconstruction index can be derived. Refresh the Body and retry.`, stake.body.address);
1128
- }
1129
- const activeStructures = group.structures.filter(({ address, deactivationTimeUnixSeconds }) => address === stake.address && deactivationTimeUnixSeconds === 0n);
1122
+ : bundledHub === undefined
1123
+ ? `Claim Stake ${stake.address} does not contain exactly one verified bundled hub. Refresh it and restore the definition-compatible bundled hub before retrying deconstruction.`
1124
+ : `Claim Stake ${stake.address} holds ${bundledHub.name} (${String(bundledHub.id)}), which is not the zero-cost ${claimDefinition.tier} bundled hub for Claim Stake definition ${String(claimDefinition.id)} (${claimDefinition.name}). Refresh the Claim Stake and Game definitions so the recorded hub matches its definition before retrying deconstruction.`, stake.address);
1125
+ }
1126
+ // Each plot row is a Starbase level; the stake's row and its position in
1127
+ // that row's chronology give the deconstruction level and index.
1128
+ const positions = claimStakePlotPositions(planet.details.claimedPlots, stake.address);
1129
+ if (positions.length !== 1) {
1130
+ fail(`Claim Stake ${stake.address} must appear exactly once in Planet ${stake.body.address}'s plot chronology before its deconstruction level and index can be derived. Refresh the Body and retry.`, stake.body.address);
1131
+ }
1132
+ const { index: claimStakeIndex, row } = positions[0];
1133
+ const rowGroups = planet.details.claimedPlots.filter(({ key }) => key === row);
1134
+ if (!Number.isInteger(row) ||
1135
+ row < 0 ||
1136
+ row >= CLAIM_STAKE_PLOT_ROWS ||
1137
+ rowGroups.length !== 1) {
1138
+ fail(`Planet ${stake.body.address} records Claim Stake ${stake.address} in plot row ${String(row)}, which is not exactly one Starbase level row. Refresh the Body and verify the pinned binding version before retrying deconstruction.`, stake.body.address);
1139
+ }
1140
+ const activeStructures = rowGroups[0].structures.filter(({ address, deactivationTimeUnixSeconds }) => address === stake.address && deactivationTimeUnixSeconds === 0n);
1130
1141
  if (activeStructures.length !== 1) {
1131
1142
  fail(`Claim Stake ${stake.address} is not represented exactly once as an active structure in the Planet Body data. Refresh the Body and retry deconstruction.`, stake.body.address);
1132
1143
  }
1133
- const claimStakeIndex = chronologyMatches[0].index;
1134
- const tierIndex = TIER_INDEX[claimDefinition.tier];
1135
- const plotCapacities = currentBody.details.claimStakePlots.map((row) => row[tierIndex]);
1136
- if (plotCapacities.some((capacity, index) => index > 0 && capacity < plotCapacities[index - 1])) {
1137
- fail(`Planet ${stake.body.address} Claim Stake plot capacity is not coherent across Starbase levels for definition tier ${claimDefinition.tier}. Refresh the Body and verify the pinned binding version before retrying deconstruction.`, stake.body.address);
1138
- }
1139
- if (group.chronoAddresses.length > plotCapacities[plotCapacities.length - 1]) {
1140
- fail(`Planet ${stake.body.address} Claim Stake plot capacity does not cover its definition-group chronology. Refresh the Body before retrying deconstruction.`, stake.body.address);
1141
- }
1142
- const plotLevelIndex = plotCapacities.findIndex((capacity) => claimStakeIndex < capacity);
1143
- const claimStakeLevel = CLAIM_STAKE_PLOT_LEVELS[plotLevelIndex];
1144
- const system = currentBody.system;
1145
- const player = await deriveStarbasePlayerAddress(system, character);
1146
- const [characterCandidate, systemCandidate, playerCandidate] = await readContextRawAccounts(ctx, [character, system, player]);
1147
- const characterAccount = requireAccount(characterCandidate, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry deconstruction.`);
1148
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry deconstruction.`);
1149
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying deconstruction.`);
1150
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
1151
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
1152
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
1153
- if (currentCharacter.playerProfile !== options.authorization.profile ||
1154
- currentCharacter.game !== game) {
1155
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry deconstruction.`, character);
1156
- }
1157
- if (currentSystem.game !== game ||
1158
- currentSystem.sequenceId !== stake.systemSeqId ||
1159
- currentSystem.starbase === undefined ||
1160
- !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
1161
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
1162
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry deconstruction.`, system);
1163
- }
1164
- if (currentPlayer.playerProfile !== options.authorization.profile ||
1165
- currentPlayer.character !== character ||
1166
- currentPlayer.system !== system) {
1167
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry deconstruction.`, player);
1168
- }
1144
+ const claimStakeLevel = CLAIM_STAKE_PLOT_LEVELS[row];
1145
+ const [currencyCache, [xpAccrual]] = await Promise.all([
1146
+ claimStakeCurrencyCache(game),
1147
+ deriveStructureXpAccrualAddress(stake.address),
1148
+ ]);
1149
+ const { currentSystem, extraAccounts: [characterCandidate, currencyCandidate, ledgerCandidate], player, playerAccount, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [character, currencyCache, xpAccrual]);
1150
+ const { account: characterAccount } = requireProfileCharacter(characterCandidate, character, profile, game, action);
1151
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake deconstruction');
1152
+ // Deconstruction closes an existing XP ledger; a stake without one accrued nothing.
1153
+ const xp = ledgerCandidate === undefined
1154
+ ? {
1155
+ accounts: {},
1156
+ guard: accountAbsentPrecondition(xpAccrual, `Claim Stake ${stake.address} XP ledger ${xpAccrual} remains absent, so no Building XP has accrued.`),
1157
+ }
1158
+ : await resolveClaimStakeXp(ctx, game, stake.address, character);
1169
1159
  const description = `Deconstruct Claim Stake ${stake.address} and discard its bundled hub ${bundledHub.name}; rent settlement, cargo return, account closure, and any refund remain chain-authoritative.`;
1170
- return createPlan({
1160
+ return buildPlannerPlan({
1171
1161
  kind: 'claim-stakes.deconstruct',
1172
- preconditions: await Promise.all([
1173
- precondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(claimDefinition.id)}, bundled hub ${String(bundledHub.id)}, and their current generations.`),
1174
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, active state, definitions, bundled hub, Body, and System generation.`),
1175
- precondition(bodyAccount, `The Planet retains Claim Stake ${stake.address} at chronology index ${String(claimStakeIndex)} in definition group ${String(claimDefinition.id)}.`),
1176
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
1177
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`),
1178
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1179
- ]),
1162
+ preconditions: [
1163
+ ...(await Promise.all([
1164
+ accountStatePrecondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(claimDefinition.id)}, bundled hub ${String(bundledHub.id)}, and their current generations.`),
1165
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, active state, definitions, bundled hub, Body, and System generation.`),
1166
+ accountStatePrecondition(bodyAccount, `The Planet retains Claim Stake ${stake.address} at chronology index ${String(claimStakeIndex)} in definition group ${String(claimDefinition.id)}.`),
1167
+ characterPrecondition(characterAccount, profile),
1168
+ systemPrecondition(systemAccount, currentSystem.name),
1169
+ playerPrecondition(playerAccount, player),
1170
+ ])),
1171
+ currencyGuard,
1172
+ xp.guard,
1173
+ ...rent.guards,
1174
+ ],
1180
1175
  steps: [
1181
1176
  {
1182
1177
  describes: description,
1183
1178
  instruction: adaptDeconstructClaimStakeInstruction({
1179
+ ...xp.accounts,
1184
1180
  authorization: options.authorization,
1185
1181
  celestialBody: stake.body.address,
1186
1182
  character,
1187
1183
  claimStakeIndex,
1188
1184
  claimStakeInstance: stake.address,
1189
1185
  claimStakeLevel,
1186
+ currencyCache,
1190
1187
  game,
1188
+ ...rent.accounts,
1191
1189
  systemAndStarbasePlayerStarbasePlayer: player,
1192
1190
  systemAndStarbasePlayerSystem: system,
1191
+ xpAccrual,
1193
1192
  }),
1194
1193
  signers: [options.authorization.authority],
1195
1194
  },
@@ -1197,13 +1196,19 @@ export async function planDeconstructClaimStake(ctx, stake, input) {
1197
1196
  summary: description,
1198
1197
  });
1199
1198
  }
1200
- /** Plans one exact raw rent top-up for an owned placed Claim Stake.
1199
+ /** Plans one exact raw rent top-up for an owned Claim Stake.
1201
1200
  *
1201
+ * Active, design and Deactivated (rent-delinquent) stakes all accept a top-up;
1202
+ * topping up a Deactivated stake is how its owner keeps it from eviction.
1202
1203
  * The planner validates the fresh Claim Stake and its canonical
1203
1204
  * Profile/Character/Planet/System/Starbase Player graph, plus the Character's
1204
1205
  * known raw ATLAS balance. Permission checks, concurrent balance changes, rent
1205
1206
  * arithmetic, and duration purchased remain chain-authoritative.
1206
1207
  *
1208
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1209
+ * settlement authority must not exist, and both stay guarded absent until
1210
+ * signing. The instruction carries no rent history.
1211
+ *
1207
1212
  * @example
1208
1213
  * ```ts
1209
1214
  * declare const ctx: SageContext;
@@ -1219,87 +1224,40 @@ export async function planDeconstructClaimStake(ctx, stake, input) {
1219
1224
  export async function planTopUpClaimStakeRent(ctx, stake, input) {
1220
1225
  const options = validateTopUpRentOptions(input);
1221
1226
  const game = requireGameAddress(ctx);
1227
+ const { profile } = options.authorization;
1228
+ // Under Disabled rent a top-up has no lifecycle gate: a Deactivated stake
1229
+ // accepts one, and that is how its owner avoids eviction.
1222
1230
  const loadedState = stake.state;
1223
- if (loadedState.kind === 'deactivated') {
1224
- fail(`Claim Stake ${stake.address} is not placed. Refresh it and choose an active Claim Stake or pending building design before topping up rent.`, stake.address);
1225
- }
1226
- if (stake.game !== game) {
1227
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
1228
- }
1229
- const character = await findCharacterPda(options.authorization.profile, game);
1230
- if (stake.ownerCharacter.address !== character) {
1231
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
1232
- }
1233
- const firstAccounts = await readContextRawAccounts(ctx, [
1234
- stake.address,
1235
- stake.body.address,
1236
- character,
1231
+ const character = await requireOwnedStake(stake, game, profile);
1232
+ const action = 'the rent top-up';
1233
+ const [{ bodyAccount, currentBody, currentStake, extraAccounts: [characterCandidate], stakeAccount, }, rent,] = await Promise.all([
1234
+ readOwnedStake(ctx, stake, action, [character]),
1235
+ requireDisabledStakeRent(ctx, game, stake.address, action),
1237
1236
  ]);
1238
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry the rent top-up.`);
1239
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry the rent top-up.`);
1240
- const characterAccount = requireAccount(firstAccounts[2], character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry the rent top-up.`);
1241
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1242
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
1243
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
1244
- if (currentStake.game !== game ||
1245
- currentStake.body !== stake.body.address ||
1246
- currentStake.ownerCharacter !== character ||
1247
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
1248
- currentStake.claimStakeDefinitionSeqId !==
1249
- stake.claimStakeDefinitionSeqId ||
1250
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
1251
- currentStake.systemSeqId !== stake.systemSeqId ||
1237
+ if (!sameOwnedStake(currentStake, stake, game, character) ||
1252
1238
  currentStake.rentBalanceRaw !== stake.rentBalanceRaw ||
1253
- !sameQuantities(currentStake.buildings, stake.buildings) ||
1254
1239
  !sameStakeState(currentStake.state, loadedState)) {
1255
- fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, rent balance, placed state, or System generation. Refresh it and retry the rent top-up.`, stake.address);
1256
- }
1257
- if (currentBody.kind !== 'planet' ||
1258
- currentBody.game !== game ||
1259
- currentBody.systemSequenceId !== stake.systemSeqId ||
1260
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
1261
- stake.body.address) {
1262
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying the rent top-up.`, stake.body.address);
1263
- }
1264
- if (currentCharacter.playerProfile !== options.authorization.profile ||
1265
- currentCharacter.game !== game) {
1266
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry the rent top-up.`, character);
1240
+ fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, rent balance, lifecycle state, or System generation. Refresh it and retry the rent top-up.`, stake.address);
1267
1241
  }
1242
+ const { system } = await requireStakePlanet(currentBody, stake, game, action);
1243
+ const { account: characterAccount, current: currentCharacter } = requireProfileCharacter(characterCandidate, character, profile, game, action);
1268
1244
  if (currentCharacter.atlasRaw < options.amount) {
1269
1245
  fail(`Character ${character} has only ${String(currentCharacter.atlasRaw)} known raw ATLAS, below the requested ${String(options.amount)} raw rent top-up. Reduce the amount or add ATLAS, refresh the Character, and retry.`, character);
1270
1246
  }
1271
- const system = currentBody.system;
1272
- const player = await deriveStarbasePlayerAddress(system, character);
1273
- const [systemCandidate, playerCandidate] = await readContextRawAccounts(ctx, [
1274
- system,
1275
- player,
1276
- ]);
1277
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry the rent top-up.`);
1278
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying the rent top-up.`);
1279
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
1280
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
1281
- if (currentSystem.game !== game ||
1282
- currentSystem.sequenceId !== stake.systemSeqId ||
1283
- currentSystem.starbase === undefined ||
1284
- !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
1285
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
1286
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, active nested Starbase, or Claim Stake Body relationship. Refresh the System and retry the rent top-up.`, system);
1287
- }
1288
- if (currentPlayer.playerProfile !== options.authorization.profile ||
1289
- currentPlayer.character !== character ||
1290
- currentPlayer.system !== system) {
1291
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry the rent top-up.`, player);
1292
- }
1247
+ const { currentSystem, player, playerAccount, systemAccount } = await readStakeStarbase(ctx, stake, game, system, character, profile, action);
1293
1248
  const description = `Top up Claim Stake ${stake.address} rent with ${String(options.amount)} raw rent units; rent arithmetic remains chain-authoritative.`;
1294
- return createPlan({
1249
+ return buildPlannerPlan({
1295
1250
  kind: 'claim-stakes.top-up-rent',
1296
- preconditions: await Promise.all([
1297
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, rent balance, placed state, Body, and System generation.`),
1298
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
1299
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} with at least ${String(options.amount)} known raw ATLAS.`),
1300
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`),
1301
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1302
- ]),
1251
+ preconditions: [
1252
+ ...(await Promise.all([
1253
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, rent balance, lifecycle state, Body, and System generation.`),
1254
+ planetPrecondition(bodyAccount, system),
1255
+ accountStatePrecondition(characterAccount, `The canonical Character remains assigned to Profile ${profile} with at least ${String(options.amount)} known raw ATLAS.`),
1256
+ systemPrecondition(systemAccount, currentSystem.name),
1257
+ playerPrecondition(playerAccount, player),
1258
+ ])),
1259
+ ...rent.guards,
1260
+ ],
1303
1261
  steps: [
1304
1262
  {
1305
1263
  describes: description,
@@ -1310,6 +1268,7 @@ export async function planTopUpClaimStakeRent(ctx, stake, input) {
1310
1268
  character,
1311
1269
  claimStakeInstance: stake.address,
1312
1270
  game,
1271
+ ...rent.accounts,
1313
1272
  systemAndStarbasePlayerStarbasePlayer: player,
1314
1273
  systemAndStarbasePlayerSystem: system,
1315
1274
  }),
@@ -1341,10 +1300,10 @@ function requireTransferCapacity(destination, address, used, requested, total) {
1341
1300
  fail(`${destination} has ${String(remaining)} raw capacity units remaining, but this transfer requires ${String(requested)}. Reduce the request or free capacity, refresh both snapshots, and retry.`, address);
1342
1301
  }
1343
1302
  }
1344
- function fleetCargoAmount(fleet, cargoId) {
1345
- return ((fleet.ammoBank.cargoId === cargoId ? fleet.ammoBank.amount : 0n) +
1346
- (fleet.fuelTank.cargoId === cargoId ? fleet.fuelTank.amount : 0n) +
1347
- cargoAmount(fleet.cargoHold.amounts, cargoId));
1303
+ /** Reads a Fleet's cargo-hold balance; Claim Stake transfers never touch the
1304
+ * fuel tank or ammo bank, so Fuel and Ammunition count only when in the hold. */
1305
+ function fleetCargoHoldAmount(fleet, cargoId) {
1306
+ return cargoAmount(fleet.cargoHold.amounts, cargoId);
1348
1307
  }
1349
1308
  function describeCargoRequests(direction, requests) {
1350
1309
  return requests.map(({ amount, cargoId }) => `${direction} cargo id ${String(cargoId)} = ${String(amount)} raw ${amount === 1n ? 'unit' : 'units'}`);
@@ -1354,8 +1313,18 @@ function describeCargoRequests(direction, requests) {
1354
1313
  * The planner validates the shared Profile/Game, idle Fleet with normalized
1355
1314
  * crew, active Claim Stake, canonical Planet/System/Starbase Player graph,
1356
1315
  * exact source balances, exact destination capacities, and an optional present
1357
- * FleetCrewBinding. The chain remains authoritative for range, duration,
1358
- * permission bits, concurrent state changes, and transfer arithmetic.
1316
+ * FleetCrewBinding. Both legs use the Fleet cargo hold only: loaded Fuel and
1317
+ * Ammunition arrive in the cargo hold and consume cargo capacity, and unloads
1318
+ * are sourced from the cargo hold, never from the fuel tank or ammo bank. The
1319
+ * chain remains authoritative for range, duration, permission bits, concurrent
1320
+ * state changes, and transfer arithmetic.
1321
+ *
1322
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
1323
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
1324
+ *
1325
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1326
+ * settlement authority must not exist, and both stay guarded absent until
1327
+ * signing. The instruction carries no rent history.
1359
1328
  *
1360
1329
  * @example
1361
1330
  * ```ts
@@ -1374,119 +1343,56 @@ function describeCargoRequests(direction, requests) {
1374
1343
  export async function planStartClaimStakeFleetTransfer(ctx, fleet, stake, input) {
1375
1344
  const options = validateStartFleetTransferOptions(input);
1376
1345
  const game = requireGameAddress(ctx);
1346
+ const { profile } = options.authorization;
1377
1347
  if (fleet.game !== game || stake.game !== game) {
1378
1348
  fail(`Fleet ${fleet.address} and Claim Stake ${stake.address} must both belong to configured Game ${game}. Refresh both snapshots through this context and retry.`, fleet.game !== game ? fleet.address : stake.address);
1379
1349
  }
1380
- if (fleet.ownerProfile.address !== options.authorization.profile) {
1381
- fail(`Fleet ${fleet.name} belongs to Profile ${fleet.ownerProfile.address}, not authorization Profile ${options.authorization.profile}. Use the owning Profile, refresh the Fleet, and retry.`, fleet.address);
1350
+ if (fleet.ownerProfile.address !== profile) {
1351
+ fail(`Fleet ${fleet.name} belongs to Profile ${fleet.ownerProfile.address}, not authorization Profile ${profile}. Use the owning Profile, refresh the Fleet, and retry.`, fleet.address);
1382
1352
  }
1383
1353
  if (fleet.state.kind !== 'idle') {
1384
1354
  fail(`Fleet ${fleet.name} is ${fleet.state.kind}, but starting a Claim Stake transfer requires an idle Fleet. Return it to idle state, refresh it, and retry.`, fleet.address);
1385
1355
  }
1356
+ const crewFailure = `Fleet ${fleet.name} has no valid normalized crew and cannot start a Claim Stake transfer. Crew the Fleet, refresh it, and retry.`;
1386
1357
  if (!Number.isSafeInteger(fleet.crewCount) || fleet.crewCount <= 0) {
1387
- fail(`Fleet ${fleet.name} has no valid normalized crew and cannot start a Claim Stake transfer. Crew the Fleet, refresh it, and retry.`, fleet.address);
1358
+ fail(crewFailure, fleet.address);
1388
1359
  }
1389
1360
  if (stake.state.kind !== 'active') {
1390
1361
  fail(`Claim Stake ${stake.address} is ${stake.state.kind}, but starting a Fleet transfer requires an active Claim Stake. Activate or restore it, refresh it, and retry.`, stake.address);
1391
1362
  }
1392
- const character = await findCharacterPda(options.authorization.profile, game);
1393
- if (stake.ownerCharacter.address !== character) {
1394
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned active Claim Stake, refresh it, and retry.`, stake.address);
1395
- }
1396
- const [fleetCandidate, stakeCandidate, bodyCandidate, characterCandidate] = await readContextRawAccounts(ctx, [
1397
- fleet.address,
1398
- stake.address,
1399
- stake.body.address,
1400
- character,
1363
+ const character = await requireOwnedStake(stake, game, profile);
1364
+ const action = 'the Fleet transfer';
1365
+ const [{ bodyAccount, currentBody, currentStake, extraAccounts: [fleetCandidate, characterCandidate], stakeAccount, }, rent,] = await Promise.all([
1366
+ readOwnedStake(ctx, stake, action, [fleet.address, character]),
1367
+ requireDisabledStakeRent(ctx, game, stake.address, action),
1401
1368
  ]);
1402
- const fleetAccount = requireAccount(fleetCandidate, fleet.address, `Fleet ${fleet.name} is missing. Refresh it and retry the Claim Stake transfer.`);
1403
- const stakeAccount = requireAccount(stakeCandidate, stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry the Fleet transfer.`);
1404
- const bodyAccount = requireAccount(bodyCandidate, stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry the Fleet transfer.`);
1405
- const characterAccount = requireAccount(characterCandidate, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry the Claim Stake transfer.`);
1369
+ const fleetAccount = requireAccount(fleetCandidate, fleet.address, `Fleet ${fleet.name} is missing. Refresh it and retry the Fleet transfer.`);
1406
1370
  const currentFleet = fleetAccountDefinition.decode(fleetAccount);
1407
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1408
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
1409
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
1410
1371
  if (!Number.isSafeInteger(currentFleet.crewCount) ||
1411
1372
  currentFleet.crewCount <= 0) {
1412
- fail(`Fleet ${fleet.name} has no valid normalized crew and cannot start a Claim Stake transfer. Crew the Fleet, refresh it, and retry.`, fleet.address);
1373
+ fail(crewFailure, fleet.address);
1413
1374
  }
1414
- if (currentFleet.game !== game ||
1415
- currentFleet.ownerProfile !== options.authorization.profile ||
1416
- currentFleet.name !== fleet.name ||
1375
+ if (!sameTransferFleet(currentFleet, fleet, game, profile) ||
1417
1376
  currentFleet.state.kind !== 'idle' ||
1418
1377
  currentFleet.crewCount !== fleet.crewCount ||
1419
- (await deriveFleetAddress(currentFleet)) !== fleet.address ||
1420
- currentFleet.ammoBank.cargoId !== fleet.ammo.id ||
1421
- currentFleet.ammoBank.amount !== fleet.ammo.amount ||
1422
- currentFleet.fuelTank.cargoId !== fleet.fuel.id ||
1423
- currentFleet.fuelTank.amount !== fleet.fuel.amount ||
1424
- currentFleet.cargoHold.storageCost !== fleet.cargoHold.storageCost ||
1425
- !sameCargoAmounts(currentFleet.cargoHold.amounts, fleet.cargoHold.items) ||
1426
- currentFleet.stats.cargo.ammoCapacity !== fleet.capacities.ammo.total ||
1427
- currentFleet.stats.cargo.fuelCapacity !== fleet.capacities.fuel.total ||
1428
- currentFleet.stats.cargo.cargoCapacity !== fleet.capacities.cargo.total) {
1378
+ (await deriveFleetAddress(currentFleet)) !== fleet.address) {
1429
1379
  fail(`Fleet ${fleet.name} changed or no longer matches its loaded Game, owner, canonical address, name, idle state, normalized crew, cargo, or capacities. Refresh it and create a new Claim Stake transfer Plan.`, fleet.address);
1430
1380
  }
1431
- if (currentStake.game !== game ||
1432
- currentStake.ownerCharacter !== character ||
1433
- currentStake.body !== stake.body.address ||
1434
- currentStake.systemSeqId !== stake.systemSeqId ||
1435
- currentStake.state.kind !== 'active' ||
1436
- currentStake.production.cargoPod.storageCost !==
1437
- stake.resources.capacity.usedRaw ||
1438
- currentStake.production.resourceCapacityRaw !==
1439
- stake.resources.capacity.totalRaw ||
1440
- !sameCargoAmounts(currentStake.production.cargoPod.amounts, stake.resources.inventory.items.map(({ id, quantityRaw }) => ({
1441
- amount: quantityRaw,
1442
- id,
1443
- })))) {
1381
+ if (!sameTransferStake(currentStake, stake, game, character)) {
1444
1382
  fail(`Claim Stake ${stake.address} changed or no longer matches its loaded Game, owner, Body, active state, resources, capacity, or System generation. Refresh it and create a new Fleet transfer Plan.`, stake.address);
1445
1383
  }
1446
- if (currentBody.kind !== 'planet' ||
1447
- currentBody.game !== game ||
1448
- currentBody.systemSequenceId !== stake.systemSeqId ||
1449
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
1450
- stake.body.address) {
1451
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying the Fleet transfer.`, stake.body.address);
1452
- }
1453
- if (currentCharacter.playerProfile !== options.authorization.profile ||
1454
- currentCharacter.game !== game) {
1455
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry the Claim Stake transfer.`, character);
1456
- }
1457
- const system = currentBody.system;
1458
- const player = await deriveStarbasePlayerAddress(system, character);
1459
- const currencyCache = await deriveCurrencyCacheAddress(game);
1460
- const crewBinding = await deriveFleetCrewBindingAddress(fleet.address);
1461
- const [systemCandidate, playerCandidate, currencyCandidate, crewCandidate] = await readContextRawAccounts(ctx, [
1462
- system,
1463
- player,
1464
- currencyCache,
1465
- crewBinding,
1384
+ const { system } = await requireStakePlanet(currentBody, stake, game, action);
1385
+ const { account: characterAccount } = requireProfileCharacter(characterCandidate, character, profile, game, action);
1386
+ const [currencyCache, crewBinding] = await Promise.all([
1387
+ claimStakeCurrencyCache(game),
1388
+ deriveFleetCrewBindingAddress(fleet.address),
1466
1389
  ]);
1467
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake System and retry the Fleet transfer.`);
1468
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake System before retrying the Fleet transfer.`);
1469
- const currencyAccount = requireAccount(currencyCandidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry the Fleet transfer.`);
1470
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
1471
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
1472
- if (currentSystem.game !== game ||
1473
- currentSystem.sequenceId !== stake.systemSeqId ||
1474
- currentSystem.starbase === undefined ||
1475
- !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
1476
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
1477
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, nested Starbase, or Claim Stake Body relationship. Refresh the System and retry the Fleet transfer.`, system);
1478
- }
1479
- if (currentPlayer.playerProfile !== options.authorization.profile ||
1480
- currentPlayer.character !== character ||
1481
- currentPlayer.system !== system) {
1482
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry the Fleet transfer.`, player);
1483
- }
1484
- validateCurrencyCache(currencyAccount, game, currencyCache, 'the Claim Stake Fleet transfer');
1390
+ const { currentSystem, extraAccounts: [currencyCandidate, crewCandidate], player, playerAccount, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [currencyCache, crewBinding]);
1391
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake Fleet transfer');
1485
1392
  if (crewCandidate !== undefined) {
1486
1393
  const currentCrewBinding = fleetCrewBindingAccountDefinition.decode(crewCandidate);
1487
1394
  if (crewCandidate.address !== crewBinding ||
1488
- currentCrewBinding.fleet !== fleet.address ||
1489
- currentCrewBinding.ownerProfile !== options.authorization.profile) {
1395
+ !matchesFleetCrewBindingIdentity(currentCrewBinding, fleet.address, profile)) {
1490
1396
  fail(`FleetCrewBinding ${crewCandidate.address} does not match Fleet ${fleet.address}, its owner Profile, or its canonical PDA. Refresh the Fleet crew registration or remove the invalid binding before retrying.`, crewCandidate.address);
1491
1397
  }
1492
1398
  }
@@ -1498,48 +1404,44 @@ export async function planStartClaimStakeFleetTransfer(ctx, fleet, stake, input)
1498
1404
  requireCargoBalance(`Claim Stake ${stake.address}`, stake.address, request.cargoId, cargoAmount(currentStake.production.cargoPod.amounts, request.cargoId), request.amount);
1499
1405
  }
1500
1406
  for (const request of options.unload) {
1501
- requireCargoBalance(`Fleet ${fleet.name}`, fleet.address, request.cargoId, fleetCargoAmount(currentFleet, request.cargoId), request.amount);
1502
- }
1503
- const loadAmmo = options.load
1504
- .filter(({ cargoId }) => cargoId === currentFleet.ammoBank.cargoId)
1505
- .reduce((total, { amount }) => total + amount, 0n);
1506
- const loadFuel = options.load
1507
- .filter(({ cargoId }) => cargoId === currentFleet.fuelTank.cargoId)
1508
- .reduce((total, { amount }) => total + amount, 0n);
1509
- const loadCargoStorage = options.load
1510
- .filter(({ cargoId }) => cargoId !== currentFleet.ammoBank.cargoId &&
1511
- cargoId !== currentFleet.fuelTank.cargoId)
1512
- .reduce((total, { amount, cargoId }) => total + amount * BigInt(definitions.get(cargoId).storageCost), 0n);
1513
- requireTransferCapacity(`Fleet ${fleet.name} ammo destination`, fleet.address, currentFleet.ammoBank.amount, loadAmmo, currentFleet.stats.cargo.ammoCapacity);
1514
- requireTransferCapacity(`Fleet ${fleet.name} fuel destination`, fleet.address, currentFleet.fuelTank.amount, loadFuel, currentFleet.stats.cargo.fuelCapacity);
1407
+ requireCargoBalance(`Fleet ${fleet.name}`, fleet.address, request.cargoId, fleetCargoHoldAmount(currentFleet, request.cargoId), request.amount);
1408
+ }
1409
+ // Every load, including Fuel and Ammunition, lands in the cargo hold.
1410
+ const loadCargoStorage = options.load.reduce((total, { amount, cargoId }) => total + cargoStorageUnits(amount, definitions.get(cargoId).storageCost), 0n);
1515
1411
  requireTransferCapacity(`Fleet ${fleet.name} cargo destination`, fleet.address, currentFleet.cargoHold.storageCost, loadCargoStorage, currentFleet.stats.cargo.cargoCapacity);
1516
- const unloadStorage = options.unload.reduce((total, { amount, cargoId }) => total + amount * BigInt(definitions.get(cargoId).storageCost), 0n);
1412
+ const unloadStorage = options.unload.reduce((total, { amount, cargoId }) => total + cargoStorageUnits(amount, definitions.get(cargoId).storageCost), 0n);
1517
1413
  requireTransferCapacity(`Claim Stake ${stake.address} resource destination`, stake.address, currentStake.production.cargoPod.storageCost, unloadStorage, currentStake.production.resourceCapacityRaw);
1518
1414
  const descriptions = [
1519
1415
  ...describeCargoRequests('load', options.load),
1520
1416
  ...describeCargoRequests('unload', options.unload),
1521
1417
  ];
1522
- const description = `Start fleet ${fleet.name}'s transfer to Claim Stake ${stake.address}: ${descriptions.join('; ')}. Range and transfer duration remain chain-authoritative.`;
1523
- return createPlan({
1418
+ const description = `Start fleet ${fleet.name}'s transfer to Claim Stake ${stake.address}: ${descriptions.join('; ')}. Loaded cargo, including Fuel and Ammunition, arrives in the Fleet cargo hold; unloads come from the cargo hold only. Range and transfer duration remain chain-authoritative.`;
1419
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address);
1420
+ return buildPlannerPlan({
1524
1421
  kind: 'claim-stakes.start-fleet-transfer',
1525
- preconditions: await Promise.all([
1526
- precondition(fleetAccount, `Fleet ${fleet.name} retains its owner, name, idle state, normalized crew, cargo balances, and destination capacities.`),
1527
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its owner, active state, cargo balances, destination capacity, Body, and System generation.`),
1528
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
1529
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
1530
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`),
1531
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1532
- precondition(currencyAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
1533
- ...(crewCandidate === undefined
1534
- ? []
1535
- : [
1536
- precondition(crewCandidate, `Fleet ${fleet.name}'s optional FleetCrewBinding remains assigned to the Fleet and owner Profile.`),
1537
- ]),
1538
- ]),
1422
+ preconditions: [
1423
+ xp.guard,
1424
+ ...(await Promise.all([
1425
+ accountStatePrecondition(fleetAccount, `Fleet ${fleet.name} retains its owner, name, idle state, normalized crew, cargo balances, and destination capacities.`),
1426
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its owner, active state, cargo balances, destination capacity, Body, and System generation.`),
1427
+ planetPrecondition(bodyAccount, system),
1428
+ characterPrecondition(characterAccount, profile),
1429
+ systemPrecondition(systemAccount, currentSystem.name),
1430
+ playerPrecondition(playerAccount, player),
1431
+ ...(crewCandidate === undefined
1432
+ ? []
1433
+ : [
1434
+ accountStatePrecondition(crewCandidate, `Fleet ${fleet.name}'s optional FleetCrewBinding remains assigned to the Fleet and owner Profile.`),
1435
+ ]),
1436
+ ])),
1437
+ currencyGuard,
1438
+ ...rent.guards,
1439
+ ],
1539
1440
  steps: [
1540
1441
  {
1541
1442
  describes: description,
1542
1443
  instruction: adaptStartClaimStakeFleetTransferInstruction({
1444
+ ...xp.accounts,
1543
1445
  authorization: options.authorization,
1544
1446
  celestialBody: stake.body.address,
1545
1447
  character,
@@ -1548,6 +1450,7 @@ export async function planStartClaimStakeFleetTransfer(ctx, fleet, stake, input)
1548
1450
  currencyCache,
1549
1451
  fleet: fleet.address,
1550
1452
  game,
1453
+ ...rent.accounts,
1551
1454
  systemAndStarbasePlayerStarbasePlayer: player,
1552
1455
  systemAndStarbasePlayerSystem: system,
1553
1456
  toLoad: options.load.map(({ amount, cargoId }) => [cargoId, amount]),
@@ -1606,10 +1509,7 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1606
1509
  if (options.atUnixSeconds < fleet.state.endAtUnixSeconds) {
1607
1510
  fail(`Fleet ${fleet.name}'s Claim Stake transfer lock has not elapsed at Unix timestamp ${String(options.atUnixSeconds)}; it ends at ${String(fleet.state.endAtUnixSeconds)}. Wait until the lock elapses, provide a current timestamp, and retry recovery.`, fleet.address);
1608
1511
  }
1609
- const character = await findCharacterPda(options.authorization.profile, game);
1610
- if (stake.ownerCharacter.address !== character) {
1611
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Supply the owned current Claim Stake and retry recovery.`, stake.address);
1612
- }
1512
+ const character = await requireOwnedStake(stake, game, options.authorization.profile);
1613
1513
  const [fleetCandidate, stakeCandidate] = await readContextRawAccounts(ctx, [
1614
1514
  fleet.address,
1615
1515
  stake.address,
@@ -1618,40 +1518,17 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1618
1518
  const stakeAccount = requireAccount(stakeCandidate, stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry Fleet transfer recovery.`);
1619
1519
  const currentFleet = fleetAccountDefinition.decode(fleetAccount);
1620
1520
  const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1621
- if (currentFleet.game !== game ||
1622
- currentFleet.ownerProfile !== options.authorization.profile ||
1623
- currentFleet.name !== fleet.name ||
1521
+ if (!sameTransferFleet(currentFleet, fleet, game, options.authorization.profile) ||
1624
1522
  currentFleet.state.kind !== 'claimStakeTransfer' ||
1625
1523
  currentFleet.state.claimStake !== stake.address ||
1626
1524
  currentFleet.state.planet !== stake.body.address ||
1627
1525
  currentFleet.state.endAtUnixSeconds !== fleet.state.endAtUnixSeconds ||
1628
1526
  !sameCargoAmounts(currentFleet.state.load, fleet.state.load) ||
1629
1527
  !sameCargoAmounts(currentFleet.state.unload, fleet.state.unload) ||
1630
- (await deriveFleetAddress(currentFleet)) !== fleet.address ||
1631
- currentFleet.ammoBank.cargoId !== fleet.ammo.id ||
1632
- currentFleet.ammoBank.amount !== fleet.ammo.amount ||
1633
- currentFleet.fuelTank.cargoId !== fleet.fuel.id ||
1634
- currentFleet.fuelTank.amount !== fleet.fuel.amount ||
1635
- currentFleet.cargoHold.storageCost !== fleet.cargoHold.storageCost ||
1636
- !sameCargoAmounts(currentFleet.cargoHold.amounts, fleet.cargoHold.items) ||
1637
- currentFleet.stats.cargo.ammoCapacity !== fleet.capacities.ammo.total ||
1638
- currentFleet.stats.cargo.fuelCapacity !== fleet.capacities.fuel.total ||
1639
- currentFleet.stats.cargo.cargoCapacity !== fleet.capacities.cargo.total) {
1528
+ (await deriveFleetAddress(currentFleet)) !== fleet.address) {
1640
1529
  fail(`Fleet ${fleet.name} changed or no longer matches its loaded owner, Game, canonical address, Claim Stake transfer snapshot, cargo, or capacities. Refresh it and retry recovery.`, fleet.address);
1641
1530
  }
1642
- if (currentStake.game !== game ||
1643
- currentStake.ownerCharacter !== character ||
1644
- currentStake.body !== stake.body.address ||
1645
- currentStake.systemSeqId !== stake.systemSeqId ||
1646
- currentStake.state.kind !== 'active' ||
1647
- currentStake.production.cargoPod.storageCost !==
1648
- stake.resources.capacity.usedRaw ||
1649
- currentStake.production.resourceCapacityRaw !==
1650
- stake.resources.capacity.totalRaw ||
1651
- !sameCargoAmounts(currentStake.production.cargoPod.amounts, stake.resources.inventory.items.map(({ id, quantityRaw }) => ({
1652
- amount: quantityRaw,
1653
- id,
1654
- })))) {
1531
+ if (!sameTransferStake(currentStake, stake, game, character)) {
1655
1532
  fail(`Claim Stake ${stake.address} changed or no longer matches its loaded owner, Game, Body, active state, resources, capacity, or System generation. Refresh it and retry Fleet transfer recovery.`, stake.address);
1656
1533
  }
1657
1534
  let reason;
@@ -1664,7 +1541,7 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1664
1541
  }
1665
1542
  if (reason === undefined) {
1666
1543
  for (const request of currentFleet.state.unload) {
1667
- const available = fleetCargoAmount(currentFleet, request.cargoId);
1544
+ const available = fleetCargoHoldAmount(currentFleet, request.cargoId);
1668
1545
  if (available < request.amount) {
1669
1546
  reason = `Fleet source shortfall for cargo id ${String(request.cargoId)}`;
1670
1547
  break;
@@ -1690,22 +1567,12 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1690
1567
  }
1691
1568
  }
1692
1569
  if (reason === undefined) {
1693
- const loadAmmo = cargoAmount(currentFleet.state.load, currentFleet.ammoBank.cargoId);
1694
- const loadFuel = cargoAmount(currentFleet.state.load, currentFleet.fuelTank.cargoId);
1695
- const loadCargoStorage = currentFleet.state.load
1696
- .filter(({ cargoId }) => cargoId !== currentFleet.ammoBank.cargoId &&
1697
- cargoId !== currentFleet.fuelTank.cargoId)
1698
- .reduce((total, { amount, cargoId }) => total + amount * BigInt(queuedDefinitions.get(cargoId).storageCost), 0n);
1699
- const unloadStorage = currentFleet.state.unload.reduce((total, { amount, cargoId }) => total + amount * BigInt(queuedDefinitions.get(cargoId).storageCost), 0n);
1700
- if (currentFleet.ammoBank.amount + loadAmmo >
1701
- currentFleet.stats.cargo.ammoCapacity) {
1702
- reason = 'Fleet ammo destination overflow';
1703
- }
1704
- else if (currentFleet.fuelTank.amount + loadFuel >
1705
- currentFleet.stats.cargo.fuelCapacity) {
1706
- reason = 'Fleet fuel destination overflow';
1707
- }
1708
- else if (currentFleet.cargoHold.storageCost + loadCargoStorage >
1570
+ // Every queued load, including Fuel and Ammunition, targets the cargo hold.
1571
+ const loadCargoStorage = currentFleet.state.load.reduce((total, { amount, cargoId }) => total +
1572
+ cargoStorageUnits(amount, queuedDefinitions.get(cargoId).storageCost), 0n);
1573
+ const unloadStorage = currentFleet.state.unload.reduce((total, { amount, cargoId }) => total +
1574
+ cargoStorageUnits(amount, queuedDefinitions.get(cargoId).storageCost), 0n);
1575
+ if (currentFleet.cargoHold.storageCost + loadCargoStorage >
1709
1576
  currentFleet.stats.cargo.cargoCapacity) {
1710
1577
  reason = 'Fleet cargo destination overflow';
1711
1578
  }
@@ -1718,15 +1585,15 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1718
1585
  fail(`Fleet ${fleet.name}'s Claim Stake transfer is currently executable. Use the permissionless normal exit path; recovery is only for a locally proven non-executable transfer.`, fleet.address);
1719
1586
  }
1720
1587
  const description = `Recover fleet ${fleet.name} from its stuck Claim Stake transfer (${reason}); no cargo moves and the Fleet returns to idle.`;
1721
- return createPlan({
1588
+ return buildPlannerPlan({
1722
1589
  kind: 'claim-stakes.recover-fleet-transfer',
1723
1590
  preconditions: await Promise.all([
1724
- precondition(fleetAccount, `Fleet ${fleet.name} retains its owner and exact elapsed, non-executable Claim Stake transfer snapshot.`),
1725
- precondition(stakeAccount, `Claim Stake ${stake.address} remains active with the current owner, cargo, and capacity used to prove non-executability.`),
1591
+ accountStatePrecondition(fleetAccount, `Fleet ${fleet.name} retains its owner and exact elapsed, non-executable Claim Stake transfer snapshot.`),
1592
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} remains active with the current owner, cargo, and capacity used to prove non-executability.`),
1726
1593
  ...(gameAccount === undefined
1727
1594
  ? []
1728
1595
  : [
1729
- precondition(gameAccount, `Game ${game} retains the cargo definitions used to prove the transfer is non-executable.`),
1596
+ accountStatePrecondition(gameAccount, `Game ${game} retains the cargo definitions used to prove the transfer is non-executable.`),
1730
1597
  ]),
1731
1598
  ]),
1732
1599
  steps: [