cellpycore 0.2.4__tar.gz → 0.2.6__tar.gz

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 (266) hide show
  1. {cellpycore-0.2.4/.issueflows/02-partly-solved-issues → cellpycore-0.2.6/.issueflows/03-solved-issues}/issue100_status.md +1 -1
  2. cellpycore-0.2.6/.issueflows/03-solved-issues/issue142_original.md +48 -0
  3. cellpycore-0.2.6/.issueflows/03-solved-issues/issue142_plan.md +86 -0
  4. cellpycore-0.2.6/.issueflows/03-solved-issues/issue142_status.md +20 -0
  5. cellpycore-0.2.6/.issueflows/03-solved-issues/issue147_original.md +15 -0
  6. cellpycore-0.2.6/.issueflows/03-solved-issues/issue147_plan.md +99 -0
  7. cellpycore-0.2.6/.issueflows/03-solved-issues/issue147_status.md +24 -0
  8. cellpycore-0.2.6/.issueflows/03-solved-issues/issue148_original.md +15 -0
  9. cellpycore-0.2.6/.issueflows/03-solved-issues/issue148_plan.md +144 -0
  10. cellpycore-0.2.6/.issueflows/03-solved-issues/issue148_status.md +27 -0
  11. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/cellpy-core-integration-roadmap.md +14 -10
  12. cellpycore-0.2.6/.vscode/settings.json +14 -0
  13. {cellpycore-0.2.4 → cellpycore-0.2.6}/HISTORY.md +13 -0
  14. {cellpycore-0.2.4 → cellpycore-0.2.6}/PKG-INFO +2 -2
  15. {cellpycore-0.2.4 → cellpycore-0.2.6}/pyproject.toml +1 -1
  16. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/cell_core.py +51 -21
  17. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/merge.py +37 -9
  18. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/summarizers.py +18 -0
  19. {cellpycore-0.2.4 → cellpycore-0.2.6}/uv.lock +2 -2
  20. cellpycore-0.2.4/.vscode/settings.json +0 -7
  21. {cellpycore-0.2.4 → cellpycore-0.2.6}/.aliases +0 -0
  22. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/rules/cellpy-core-migration.mdc +0 -0
  23. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/rules/graphify.mdc +0 -0
  24. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/rules/issueflow-rules.mdc +0 -0
  25. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/rules/kiss.mdc +0 -0
  26. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/rules/this-project.mdc +0 -0
  27. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/caveman/SKILL.md +0 -0
  28. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/grill-me/SKILL.md +0 -0
  29. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow/SKILL.md +0 -0
  30. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-archive/SKILL.md +0 -0
  31. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-cleanup/SKILL.md +0 -0
  32. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-close/SKILL.md +0 -0
  33. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-comments/SKILL.md +0 -0
  34. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-cycle/SKILL.md +0 -0
  35. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-epic/SKILL.md +0 -0
  36. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-fix/SKILL.md +0 -0
  37. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-graphify/SKILL.md +0 -0
  38. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-history-update/SKILL.md +0 -0
  39. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-init/SKILL.md +0 -0
  40. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-pause/SKILL.md +0 -0
  41. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-pick/SKILL.md +0 -0
  42. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-plan/SKILL.md +0 -0
  43. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-start/SKILL.md +0 -0
  44. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-status/SKILL.md +0 -0
  45. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-version-bump/SKILL.md +0 -0
  46. {cellpycore-0.2.4 → cellpycore-0.2.6}/.cursor/skills/iflow-yolo/SKILL.md +0 -0
  47. {cellpycore-0.2.4 → cellpycore-0.2.6}/.gitattributes +0 -0
  48. {cellpycore-0.2.4 → cellpycore-0.2.6}/.gitignore +0 -0
  49. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/00-tools/.gitkeep +0 -0
  50. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/00-tools/README.md +0 -0
  51. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/01-current-issues/.gitkeep +0 -0
  52. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/02-partly-solved-issues/.gitkeep +0 -0
  53. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/.gitkeep +0 -0
  54. {cellpycore-0.2.4/.issueflows/02-partly-solved-issues → cellpycore-0.2.6/.issueflows/03-solved-issues}/issue100_original.md +0 -0
  55. {cellpycore-0.2.4/.issueflows/02-partly-solved-issues → cellpycore-0.2.6/.issueflows/03-solved-issues}/issue100_plan.md +0 -0
  56. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue10_original.md +0 -0
  57. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue10_plan.md +0 -0
  58. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue10_status.md +0 -0
  59. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue110_original.md +0 -0
  60. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue110_plan.md +0 -0
  61. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue110_status.md +0 -0
  62. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue112_original.md +0 -0
  63. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue112_plan.md +0 -0
  64. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue112_status.md +0 -0
  65. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue114_original.md +0 -0
  66. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue114_plan.md +0 -0
  67. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue114_status.md +0 -0
  68. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue115_original.md +0 -0
  69. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue115_plan.md +0 -0
  70. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue115_status.md +0 -0
  71. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue116_original.md +0 -0
  72. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue116_plan.md +0 -0
  73. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue116_status.md +0 -0
  74. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue117_original.md +0 -0
  75. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue117_plan.md +0 -0
  76. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue117_status.md +0 -0
  77. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue118_original.md +0 -0
  78. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue118_plan.md +0 -0
  79. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue118_status.md +0 -0
  80. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue121_original.md +0 -0
  81. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue121_plan.md +0 -0
  82. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue121_status.md +0 -0
  83. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue12_original.md +0 -0
  84. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue12_plan.md +0 -0
  85. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue12_status.md +0 -0
  86. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue136_original.md +0 -0
  87. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue136_plan.md +0 -0
  88. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue136_status.md +0 -0
  89. {cellpycore-0.2.4/.issueflows/01-current-issues → cellpycore-0.2.6/.issueflows/03-solved-issues}/issue138_original.md +0 -0
  90. {cellpycore-0.2.4/.issueflows/01-current-issues → cellpycore-0.2.6/.issueflows/03-solved-issues}/issue138_plan.md +0 -0
  91. {cellpycore-0.2.4/.issueflows/01-current-issues → cellpycore-0.2.6/.issueflows/03-solved-issues}/issue138_status.md +0 -0
  92. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue139_original.md +0 -0
  93. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue139_plan.md +0 -0
  94. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue139_status.md +0 -0
  95. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue13_original.md +0 -0
  96. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue13_plan.md +0 -0
  97. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue13_status.md +0 -0
  98. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue21_original.md +0 -0
  99. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue21_plan.md +0 -0
  100. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue21_status.md +0 -0
  101. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue22_original.md +0 -0
  102. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue22_plan.md +0 -0
  103. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue22_status.md +0 -0
  104. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue23_original.md +0 -0
  105. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue23_plan.md +0 -0
  106. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue23_status.md +0 -0
  107. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue24_original.md +0 -0
  108. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue24_status.md +0 -0
  109. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue29_original.md +0 -0
  110. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue29_plan.md +0 -0
  111. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue29_status.md +0 -0
  112. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue30_original.md +0 -0
  113. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue30_plan.md +0 -0
  114. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue30_status.md +0 -0
  115. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue32_original.md +0 -0
  116. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue32_plan.md +0 -0
  117. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue32_status.md +0 -0
  118. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue34_original.md +0 -0
  119. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue34_plan.md +0 -0
  120. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue36_original.md +0 -0
  121. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue36_status.md +0 -0
  122. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue39_original.md +0 -0
  123. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue39_plan.md +0 -0
  124. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue39_status.md +0 -0
  125. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue40_original.md +0 -0
  126. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue40_plan.md +0 -0
  127. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue40_status.md +0 -0
  128. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue41_original.md +0 -0
  129. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue41_plan.md +0 -0
  130. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue41_status.md +0 -0
  131. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue42_original.md +0 -0
  132. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue42_plan.md +0 -0
  133. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue42_status.md +0 -0
  134. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue43_original.md +0 -0
  135. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue43_plan.md +0 -0
  136. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue43_status.md +0 -0
  137. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue44_original.md +0 -0
  138. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue44_status.md +0 -0
  139. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue45_original.md +0 -0
  140. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue45_plan.md +0 -0
  141. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue45_status.md +0 -0
  142. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue50_original.md +0 -0
  143. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue50_plan.md +0 -0
  144. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue50_status.md +0 -0
  145. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue54_original.md +0 -0
  146. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue54_plan.md +0 -0
  147. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue54_status.md +0 -0
  148. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue55_original.md +0 -0
  149. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue55_plan.md +0 -0
  150. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue55_status.md +0 -0
  151. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue56_original.md +0 -0
  152. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue56_plan.md +0 -0
  153. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue56_status.md +0 -0
  154. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue62_original.md +0 -0
  155. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue62_plan.md +0 -0
  156. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue62_status.md +0 -0
  157. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue64_original.md +0 -0
  158. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue64_plan.md +0 -0
  159. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue64_status.md +0 -0
  160. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue66_original.md +0 -0
  161. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue66_plan.md +0 -0
  162. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue66_status.md +0 -0
  163. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue67_original.md +0 -0
  164. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue67_plan.md +0 -0
  165. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue67_status.md +0 -0
  166. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue68_original.md +0 -0
  167. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue68_plan.md +0 -0
  168. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue68_status.md +0 -0
  169. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue69_original.md +0 -0
  170. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue69_plan.md +0 -0
  171. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue69_status.md +0 -0
  172. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue70_original.md +0 -0
  173. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue70_plan.md +0 -0
  174. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue70_status.md +0 -0
  175. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue72_original.md +0 -0
  176. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue72_status.md +0 -0
  177. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue77_original.md +0 -0
  178. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue77_plan.md +0 -0
  179. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue77_status.md +0 -0
  180. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue82_original.md +0 -0
  181. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue82_status.md +0 -0
  182. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue84_original.md +0 -0
  183. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue84_plan.md +0 -0
  184. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue84_status.md +0 -0
  185. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue86_original.md +0 -0
  186. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue86_plan.md +0 -0
  187. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue86_status.md +0 -0
  188. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue91_original.md +0 -0
  189. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue91_plan.md +0 -0
  190. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue91_status.md +0 -0
  191. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue95_original.md +0 -0
  192. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue95_plan.md +0 -0
  193. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue95_status.md +0 -0
  194. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue96_original.md +0 -0
  195. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue96_plan.md +0 -0
  196. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue96_status.md +0 -0
  197. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue97_original.md +0 -0
  198. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue97_plan.md +0 -0
  199. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue97_status.md +0 -0
  200. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue98_original.md +0 -0
  201. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue98_plan.md +0 -0
  202. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue98_status.md +0 -0
  203. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue99_original.md +0 -0
  204. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue99_plan.md +0 -0
  205. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/03-solved-issues/issue99_status.md +0 -0
  206. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/.gitkeep +0 -0
  207. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/bdf-io-placement.md +0 -0
  208. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/cellpy-core-integration-into-cellpy.md +0 -0
  209. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/cellpy-core-migration.md +0 -0
  210. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/code-review-2026-07.md +0 -0
  211. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/column-headers-review.md +0 -0
  212. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/legacy-bridge-test-id.md +0 -0
  213. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/legacy-raw-energy-mapping.md +0 -0
  214. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/metadata-scaffolding.md +0 -0
  215. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/nom-cap-naming.md +0 -0
  216. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/rawcols-dtype-map.md +0 -0
  217. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/release-procedure.md +0 -0
  218. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/selector-dead-code-deferral.md +0 -0
  219. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/step-c-rate-split.md +0 -0
  220. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/step-table-polars-migration.md +0 -0
  221. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/summary-extractors.md +0 -0
  222. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/test-data-and-fixtures.md +0 -0
  223. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/test-metadata-and-merging.md +0 -0
  224. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/this-project.md +0 -0
  225. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/04-designs-and-guides/zensical-docs.md +0 -0
  226. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/05-epics/.gitkeep +0 -0
  227. {cellpycore-0.2.4 → cellpycore-0.2.6}/.issueflows/config.toml +0 -0
  228. {cellpycore-0.2.4 → cellpycore-0.2.6}/.pre-commit-config.yaml +0 -0
  229. {cellpycore-0.2.4 → cellpycore-0.2.6}/.python-version +0 -0
  230. {cellpycore-0.2.4 → cellpycore-0.2.6}/.readthedocs.yaml +0 -0
  231. {cellpycore-0.2.4 → cellpycore-0.2.6}/AGENTS.md +0 -0
  232. {cellpycore-0.2.4 → cellpycore-0.2.6}/LICENSE +0 -0
  233. {cellpycore-0.2.4 → cellpycore-0.2.6}/README.md +0 -0
  234. {cellpycore-0.2.4 → cellpycore-0.2.6}/ROADMAP.md +0 -0
  235. {cellpycore-0.2.4 → cellpycore-0.2.6}/SCRATCHPAD.md +0 -0
  236. {cellpycore-0.2.4 → cellpycore-0.2.6}/graphify-out/.graphify_labels.json +0 -0
  237. {cellpycore-0.2.4 → cellpycore-0.2.6}/graphify-out/.graphify_root +0 -0
  238. {cellpycore-0.2.4 → cellpycore-0.2.6}/graphify-out/GRAPH_REPORT.md +0 -0
  239. {cellpycore-0.2.4 → cellpycore-0.2.6}/graphify-out/graph.html +0 -0
  240. {cellpycore-0.2.4 → cellpycore-0.2.6}/graphify-out/graph.json +0 -0
  241. {cellpycore-0.2.4 → cellpycore-0.2.6}/graphify-out/manifest.json +0 -0
  242. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/__init__.py +0 -0
  243. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/config.py +0 -0
  244. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/curves.py +0 -0
  245. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/exceptions.py +0 -0
  246. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/extractors.py +0 -0
  247. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/__init__.py +0 -0
  248. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/headers.py +0 -0
  249. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/limits.py +0 -0
  250. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/mapping.py +0 -0
  251. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/meta.py +0 -0
  252. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/meta_mapping.py +0 -0
  253. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/mock_core.py +0 -0
  254. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/legacy/selectors.py +0 -0
  255. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/metadata/__init__.py +0 -0
  256. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/metadata/io.py +0 -0
  257. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/metadata/models.py +0 -0
  258. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/py.typed +0 -0
  259. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/settings_base.py +0 -0
  260. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/testing/__init__.py +0 -0
  261. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/testing/mock_data.py +0 -0
  262. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/timestamps.py +0 -0
  263. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/units/__init__.py +0 -0
  264. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/units/converters.py +0 -0
  265. {cellpycore-0.2.4 → cellpycore-0.2.6}/src/cellpycore/units/spec.py +0 -0
  266. {cellpycore-0.2.4 → cellpycore-0.2.6}/zensical.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  # Issue #100 — status
2
2
 
3
- - [ ] Done
3
+ - [x] Done
4
4
 
5
5
  ## What's done
6
6
 
@@ -0,0 +1,48 @@
1
+ # Issue #142: Harden cycle_mode unwrapping in OldCellpyCellCore / _cycle_mode_to_test_mode
2
+
3
+ Source: https://github.com/cellpy/cellpy-core/issues/142
4
+
5
+ ## Original issue text
6
+
7
+ ## Context
8
+
9
+ Found while working **jepegit/cellpy#668** (v1.x batch notebook): after a custom `update_cell` → `make_step_table()` → `make_summary()`, the legacy bridge crashes with:
10
+
11
+ ```text
12
+ AttributeError: 'list' object has no attribute 'strip'
13
+ ```
14
+
15
+ in `cellpycore.cell_core._cycle_mode_to_test_mode` when `OldCellpyCellCore.cycle_mode` is still list-shaped (often double-nested from cellpy-file meta, e.g. `[['anode']]`).
16
+
17
+ ### Why this is a core issue (not just v1.x)
18
+
19
+ - `cellpy` **master** already mitigates on the **consumer load path** via recursive `test_meta._unwrap` after `meta_test_dependent.update(as_list=True, …)` (and has unit tests).
20
+ - The **engine/bridge** still assumes a scalar string:
21
+ - `OldCellpyCellCore.cycle_mode` getter only does one-level `m[0]`
22
+ - setter **keeps** list values (`[x.lower() for x in cycle_mode]`) instead of storing a scalar
23
+ - `_cycle_mode_to_test_mode` calls `cycle_mode.strip()` with no list/tuple handling
24
+
25
+ Any path that skips consumer unwrap (legacy loads, in-memory meta mutation, older cellpy pins) can still hit this. v1.x (#668) will add a consumer-side unwrap backport; core should still be defensive.
26
+
27
+ ## What to do
28
+
29
+ 1. **Getter** (`OldCellpyCellCore.cycle_mode`): recursively unwrap 1-element `list`/`tuple` to a scalar (same semantics as cellpy `test_meta._unwrap`).
30
+ 2. **Setter**: unwrap first, then store a **scalar** string (or `None`) — do not persist a list of lowered strings.
31
+ 3. **`_cycle_mode_to_test_mode`**: if given a list/tuple, unwrap before `.strip()`; keep existing string / `None` / unknown-spelling behaviour.
32
+ 4. **Tests** covering at least:
33
+ - `'anode'` / `'full_cell'`
34
+ - `['anode']`
35
+ - `[['anode']]`
36
+ - `None`
37
+ - make_summary / make_core_summary does not raise on nested list meta
38
+
39
+ ## Acceptance
40
+
41
+ - Nested list `cycle_mode` no longer crashes summarization through the legacy bridge.
42
+ - Existing CE / inverted-convention tests (issue #129 era) stay green.
43
+ - No API break for callers that already pass a plain string.
44
+
45
+ ## Links
46
+
47
+ - Downstream report: https://github.com/jepegit/cellpy/issues/668
48
+ - Related consumer mitigation on master: unwrap in `cellpy.readers.cellpy_file.read` / `test_meta._unwrap`
@@ -0,0 +1,86 @@
1
+ # Issue #142 — plan: harden `cycle_mode` unwrapping
2
+
3
+ ## Goal
4
+
5
+ Stop nested list-shaped `cycle_mode` (e.g. `[['anode']]`) from crashing
6
+ `_cycle_mode_to_test_mode` / summarization on the legacy bridge; store and
7
+ expose a scalar string (or `None`) from the `cycle_mode` property.
8
+
9
+ ## Constraints
10
+
11
+ - Back-compat: plain string callers unchanged; issue #127 spelling tables and
12
+ issue #129 bridge CE polarity tests stay green.
13
+ - KISS: no new package/module; private helper next to existing translator in
14
+ `cell_core.py`. No numpy dependency (cellpy’s `_unwrap` also peels numpy
15
+ scalars / NaN — out of scope here; meta on the bridge is Python
16
+ `list`/`tuple`/`str`/`None`).
17
+ - Metadata boundary unchanged: still optional; only normalize this one field
18
+ at the property / translator edge.
19
+ - Scope: getter, setter, `_cycle_mode_to_test_mode` (+ tests). No cellpy
20
+ consumer changes (jepegit/cellpy#668 stays separate).
21
+
22
+ ### Prior art
23
+
24
+ - `cellpy.readers.test_meta._unwrap` — recursive peel of 1-element
25
+ `list`/`tuple`; multi-element left as-is. **Mirror** that rule in core
26
+ (list/tuple only).
27
+ - `_cycle_mode_to_test_mode` + `#127` tests in `tests/test_schema.py` —
28
+ spelling / warning contract; **extend**, don’t rewrite.
29
+ - `CellpyCellCore.cycle_mode` getter already does one-level `m[0]`; setter
30
+ wrongly keeps a lowered **list**. `OldCellpyCellCore` inherits this
31
+ property (no override).
32
+ - `test_bridge_summary_respects_cycle_mode` (`tests/test_golden.py`) — CE
33
+ polarity regression for string `"anode"`; keep green; add a nested-list
34
+ smoke if cheap.
35
+ - Toolbox: empty (nothing reusable). Graph: `cycle_mode` /
36
+ `_cycle_mode_to_test_mode` / `OldCellpyCellCore` communities confirm the
37
+ change set is `cell_core.py` + `test_schema.py`.
38
+
39
+ ## Approach
40
+
41
+ 1. Add `_unwrap_meta_scalar(value)` in `cell_core.py` (module-private):
42
+ while `list`/`tuple` and `len == 1`, recurse; else return value.
43
+ 2. **Getter:** unwrap `meta_test_dependent.cycle_mode`; if still
44
+ `list`/`tuple`, take `[0]` when non-empty else `None` (preserves today’s
45
+ first-element behaviour for multi-element placeholders).
46
+ 3. **Setter:** unwrap first; if still sequence, take first or `None`; then
47
+ store **scalar** `str.lower()` or `None` on both meta and `_cycle_mode`
48
+ (never a list of lowered strings). Handle `None` without calling `.lower()`.
49
+ 4. **`_cycle_mode_to_test_mode`:** unwrap (+ first-element if still sequence)
50
+ before `.strip()`; keep existing `None` / empty / known / unknown+warn
51
+ behaviour. Type hint can widen to `Optional[str | list | tuple]` or stay
52
+ loose — behaviour matters more than the annotation.
53
+ 5. No public API export of the helper.
54
+
55
+ Multi-element lists (`['anode','cathode']`) are not a real cellpy-file shape
56
+ today; taking `[0]` matches the old getter and avoids a new failure mode.
57
+
58
+ ## Files to touch
59
+
60
+ | Path | Change |
61
+ |------|--------|
62
+ | [`src/cellpycore/cell_core.py`](../../src/cellpycore/cell_core.py) | `_unwrap_meta_scalar`; harden getter / setter / `_cycle_mode_to_test_mode` |
63
+ | [`tests/test_schema.py`](../../tests/test_schema.py) | Parametrize unwrap cases next to `#127` block; nested-list → INVERTED; setter stores scalar; optional `make_core_summary` / `make_summary` smoke with nested meta |
64
+
65
+ ## Test strategy
66
+
67
+ ```bash
68
+ uv run pytest tests/test_schema.py -k cycle_mode
69
+ uv run pytest # full suite before close
70
+ uv run ruff check && uv run ruff format --check
71
+ ```
72
+
73
+ New coverage (minimum from issue):
74
+
75
+ - `_cycle_mode_to_test_mode`: `'anode'`, `'full_cell'`, `['anode']`,
76
+ `[['anode']]`, `None` (and keep existing spelling params green).
77
+ - Getter/setter: set `[['anode']]` or assign list → property returns
78
+ `"anode"`; stored meta is a `str`, not a list.
79
+ - Smoke: `OldCellpyCellCore` (or `CellpyCellCore`) with nested list on
80
+ `meta_test_dependent.cycle_mode` → `make_core_summary` / `make_summary`
81
+ does not raise.
82
+
83
+ ## Open questions
84
+
85
+ None blocking — defaults above match the issue + cellpy `_unwrap`. Say if
86
+ you want multi-element lists to warn instead of silently taking `[0]`.
@@ -0,0 +1,20 @@
1
+ # Issue #142 — status
2
+
3
+ - [x] Done
4
+
5
+ ## What's done
6
+
7
+ - Plan confirmed (`issue142_plan.md`).
8
+ - Added `_unwrap_meta_scalar` / `_as_cycle_mode_scalar` in `cell_core.py`.
9
+ - Hardened `CellpyCellCore.cycle_mode` getter/setter to store/return scalar
10
+ (or `None`); nested lists no longer persist as lowered lists.
11
+ - `_cycle_mode_to_test_mode` unwraps list/tuple before `.strip()`.
12
+ - Tests in `tests/test_schema.py` (issue #142 block): translator shapes,
13
+ setter scalar store, getter unwrap, `OldCellpyCellCore.make_core_summary`
14
+ smoke with `[['anode']]` meta.
15
+ - Full suite: 281 passed; ruff green.
16
+ - `HISTORY.md` Unreleased bullet added.
17
+
18
+ ## Remaining work
19
+
20
+ - None (close / PR).
@@ -0,0 +1,15 @@
1
+ # Issue #147: update_core_data: empty new_raw with refresh_derived duplicates c-rate columns
2
+
3
+ Source: https://github.com/cellpy/cellpy-core/issues/147
4
+
5
+ ## Original issue text
6
+
7
+ Found by the cellpy L6 golden-equality oracle (jepegit/cellpy#778).
8
+
9
+ `CellpyCellCore.update_core_data(data, new_raw=<empty frame>)` short-circuits inside `merge.update_data` (returns `_copy_data(data)`), but `refresh_derived=True` (default) then runs `summarizers.c_rates_to_summary` on a summary that already carries `charge_c_rate` / `discharge_c_rate`. The join adds `charge_c_rate_right` / `discharge_c_rate_right`.
10
+
11
+ Expected: an empty `new_raw` is a no-op (frames identical to the input).
12
+
13
+ Repro (cellpy 2.1.4 / cellpycore 0.2.5): `tests/test_incremental_update.py::test_empty_tail_is_noop` in jepegit/cellpy (strict xfail until fixed).
14
+
15
+ Suggested fix: skip the `refresh_derived` block when `new_raw` is empty, or make `c_rates_to_summary` drop existing c-rate columns before joining (idempotent).
@@ -0,0 +1,99 @@
1
+ # Issue #147 — plan
2
+
3
+ Source: https://github.com/cellpy/cellpy-core/issues/147
4
+
5
+ ## Goal
6
+
7
+ `CellpyCellCore.update_core_data(data, new_raw=<empty>)` must be a true no-op:
8
+ returned frames identical to the input (no `charge_c_rate_right` /
9
+ `discharge_c_rate_right` / `ir_*_right` columns).
10
+
11
+ ## Constraints
12
+
13
+ - Polars-native engine; accept pandas frames for convenience (existing pattern).
14
+ - Input `Data` must not be mutated (`update_data` contract; keep the `_copy_data` return).
15
+ - Non-empty `new_raw` path unchanged (oracle tests in `tests/test_merge.py` stay green).
16
+ - Google-style docstrings; `ruff check` + `ruff format --check` clean.
17
+ - Downstream: cellpy `tests/test_incremental_update.py::test_empty_tail_is_noop` is
18
+ `xfail(strict=True)` against this issue. Once cellpy bumps its `cellpycore` pin to a
19
+ release containing this fix, that xfail marker must be removed (cellpy follow-up, out
20
+ of scope here — note in PR body).
21
+
22
+ ### Prior art
23
+
24
+ - `merge.update_data` (`src/cellpycore/merge.py:221`): already short-circuits on
25
+ `new_raw is None or _frame_is_empty(new_raw)` → `_copy_data(data)`. The bug is that
26
+ `update_core_data` does not honour that short-circuit before `refresh_derived`.
27
+ - `merge._frame_is_empty` (`merge.py:389`): polars/pandas-agnostic emptiness check —
28
+ reuse it in `cell_core.py` instead of writing a new one.
29
+ - `summarizers.c_rates_to_summary` (`summarizers.py:1172`) and
30
+ `summarizers.ir_to_summary` (`summarizers.py:1274`): both do a bare
31
+ `summary.join(..., how="left")` and are therefore **not idempotent** — calling either
32
+ twice on the same summary yields `*_right` columns. `ir_to_summary` has the identical
33
+ latent defect (only masked because the issue repro has no IR column in raw).
34
+ - `tests/test_merge.py::test_update_core_data_refresh_derived` (line 336): existing
35
+ test for the derived-refresh path; new tests go beside it, reusing `_single_test_raw`,
36
+ `_process_raw`, `_schema` helpers.
37
+ - Toolbox (`.issueflows/00-tools/`) empty; graphify report not needed (three
38
+ known call sites, all located by grep).
39
+
40
+ ## Approach
41
+
42
+ Two small changes — a direct no-op fix plus idempotency hardening of the two
43
+ summarizers so the same class of bug cannot recur from another caller.
44
+
45
+ 1. **`update_core_data`: return early on empty `new_raw`.**
46
+ After the pandas→polars coercion, if `_frame_is_empty(new_raw)` (or `new_raw is None`),
47
+ return `update_data(...)`'s result directly (it already returns `_copy_data(data)`)
48
+ and skip the whole `refresh_derived` block. Cheapest, and matches the documented
49
+ "empty tail is a no-op" expectation. Docstring: add one sentence stating that an
50
+ empty `new_raw` returns an unmodified copy and skips derived refresh.
51
+
52
+ 2. **Make `c_rates_to_summary` and `ir_to_summary` idempotent.**
53
+ Before the join, drop the target columns if already present:
54
+ `summary = summary.drop([c for c in (charge_c_rate, discharge_c_rate) if c in summary.columns])`
55
+ (same for `ir_charge` / `ir_discharge`). Re-running then overwrites instead of
56
+ producing `*_right`. This is the "either" alternative from the issue; doing both
57
+ keeps the no-op guarantee cheap (1) and removes the footgun for any other caller (2).
58
+
59
+ Order: 1 → 2 → tests → ruff.
60
+
61
+ ## Files to touch
62
+
63
+ - `src/cellpycore/cell_core.py` — `update_core_data`: early return on empty `new_raw`
64
+ (import `_frame_is_empty` alongside `update_data` from `cellpycore.merge`); docstring note.
65
+ - `src/cellpycore/summarizers.py` — `c_rates_to_summary`, `ir_to_summary`: drop
66
+ pre-existing target columns before the join; one docstring line each ("idempotent:
67
+ existing columns are replaced").
68
+ - `tests/test_merge.py` — new tests (see below).
69
+ - `CHANGELOG.md` (if present — check at close) — fix entry.
70
+
71
+ ## Test strategy
72
+
73
+ Run: `uv run pytest` then `uv run ruff check && uv run ruff format --check`.
74
+
75
+ New tests in `tests/test_merge.py`:
76
+
77
+ - `test_update_core_data_empty_new_raw_is_noop` — build `base` via `_process_raw`, run
78
+ `cell.update_core_data(base, extension.clear())` (polars empty frame with same
79
+ schema) with default `refresh_derived=True`; assert `updated.summary.columns ==
80
+ base.summary.columns`, no column ends with `_right`, and `raw`/`steps`/`summary`
81
+ `.equals()` the input. Also assert input `base` untouched.
82
+ - `test_update_core_data_empty_pandas_new_raw_is_noop` — same with
83
+ `extension.to_pandas().iloc[0:0]` (the cellpy repro shape).
84
+ - `test_c_rates_to_summary_idempotent` — call twice on processed data; columns
85
+ unchanged after second call, values equal.
86
+ - `test_ir_to_summary_idempotent` — same for IR (needs raw with
87
+ `internal_resistance` column; check `_single_test_raw` provides it, else add it).
88
+
89
+ Existing oracle tests (`test_update_overlap_matches_full_recompute_oracle`,
90
+ `test_update_gap_append_matches_full_recompute_oracle`,
91
+ `test_update_core_data_refresh_derived`, `tests/test_e2e.py`) guard the non-empty path.
92
+
93
+ ## Open questions
94
+
95
+ - Scope of step 2 (idempotency hardening of both summarizers): include it, or ship only
96
+ the early return (step 1)? Recommendation: include — two-line change each, closes the
97
+ same defect in `ir_to_summary`, and the issue lists it as an acceptable fix.
98
+ - Version bump at close: `patch` (bug fix, no API change). Cellpy needs a new
99
+ `cellpycore` release to drop its xfail.
@@ -0,0 +1,24 @@
1
+ # Issue #147 — status
2
+
3
+ - [x] Done
4
+
5
+ ## What's done
6
+
7
+ - Plan accepted (2026-09-08): early return in `update_core_data` on empty `new_raw`
8
+ + idempotent `c_rates_to_summary` / `ir_to_summary`.
9
+ - `cell_core.update_core_data`: returns the `update_data` copy directly when `new_raw`
10
+ is `None` or empty (skips `refresh_derived`); docstring updated.
11
+ - `summarizers.c_rates_to_summary` / `ir_to_summary`: drop pre-existing target columns
12
+ before the join via new `_drop_existing` helper → idempotent; docstrings updated.
13
+ - Tests (`tests/test_merge.py`): `test_update_core_data_empty_new_raw_is_noop`,
14
+ `test_update_core_data_empty_pandas_new_raw_is_noop`,
15
+ `test_c_rates_to_summary_idempotent`, `test_ir_to_summary_idempotent`.
16
+ Verified all 4 fail on `main` code, pass with fix.
17
+ - Full suite: 285 passed. `ruff check` + `ruff format --check` clean.
18
+ - `HISTORY.md` bullet under `[Unreleased]`. No version bump (0.2.5 already pending release).
19
+
20
+ ## Remaining work
21
+
22
+ - None in this repo.
23
+ - Follow-up (cellpy repo, after `cellpycore` pin bump to a release with this fix): remove
24
+ strict `xfail` on `tests/test_incremental_update.py::test_empty_tail_is_noop`.
@@ -0,0 +1,15 @@
1
+ # Issue #148: update_data: gap-append keeps the partial trailing step (step spanning the cut is split)
2
+
3
+ Source: https://github.com/cellpy/cellpy-core/issues/148
4
+
5
+ ## Original issue text
6
+
7
+ Found by the cellpy L6 golden-equality oracle (jepegit/cellpy#778).
8
+
9
+ When `new_raw` starts strictly after the existing `datapoint_num` max (`gap_append=True`), `merge.update_data` keeps *all* existing step rows and rebuilds only from the first new datapoint. If the existing raw ends mid-step (the tester step continues in the new rows), that step ends up as two rows: the kept partial row and a new partial row. Downstream the per-cycle summary C-rate drifts (e.g. `discharge_c_rate` 248.6919 vs 248.68871 on `neware_uio.csv` cut at row 4000).
10
+
11
+ With any overlap (>= 1 already-seen row) `_trim_steps_for_overlap` rebuilds from the start of the spanning step and the result is identical to a full load.
12
+
13
+ Expected: gap-append should also trim the trailing step of `kept_steps` when the first new row continues the same (cycle, step) and rebuild from that step's `datapoint_num_first`; alternatively document that callers must re-read from the start of the last step.
14
+
15
+ Repro: `tests/test_incremental_update.py::test_gap_append_mid_step_equals_full_load` in jepegit/cellpy (strict xfail until fixed).
@@ -0,0 +1,144 @@
1
+ # Issue #148 — plan
2
+
3
+ Source: https://github.com/cellpy/cellpy-core/issues/148
4
+
5
+ ## Goal
6
+
7
+ `merge.update_data` gap-append (`new_raw` starts strictly after existing
8
+ `datapoint_num` / `source_datapoint_num` max) must not split a tester step that
9
+ continues across the cut. Resulting steps + summary match a full
10
+ `make_step_table` / `make_summary` on the concatenated raw (same contract as
11
+ the overlap path).
12
+
13
+ ## Constraints
14
+
15
+ - Polars-native; do not mutate the input `Data` (`_copy_data` / new frames).
16
+ - Overlap path (`r2_start <= r1_max`) stays unchanged — already trims via
17
+ `_trim_steps_for_overlap` and the existing oracle stays green.
18
+ - Gap-append that starts on a **new** (cycle, step) stays as today: keep every
19
+ existing step row, rebuild from the first new `datapoint_num`.
20
+ - Single-`test_id` only (`update_data` v1). Match `(cycle_num, step_num)` as
21
+ the issue states; do not invent `sub_step_num` matching (engine still
22
+ hardcodes sub-step `1`).
23
+ - Google-style docstrings; `ruff check` + `ruff format --check` clean.
24
+ - Downstream: cellpy `tests/test_incremental_update.py::test_gap_append_mid_step_equals_full_load`
25
+ is `xfail(strict=True)` against this issue. Dropping that marker is a cellpy
26
+ follow-up after the next `cellpycore` pin bump — note in PR body, out of
27
+ scope here.
28
+
29
+ ### Prior art
30
+
31
+ - `merge.update_data` (`src/cellpycore/merge.py`): `gap_append = r2_start > r1_max`
32
+ keeps **all** `steps_pl` and sets `from_data_point` to `min(new datapoint_num)`.
33
+ That is the bug. Overlap branch already calls `_trim_steps_for_overlap`.
34
+ - `_trim_steps_for_overlap` (`merge.py`): finds the step whose
35
+ `[datapoint_num_first, datapoint_num_last]` contains the first overlapping
36
+ datapoint, drops that step and later ones, returns `from_data_point` = that
37
+ step's first. On a **gap**, the first new datapoint is *after* every existing
38
+ `datapoint_num_last`, so this helper currently keeps all steps — it cannot
39
+ detect continuation by range alone.
40
+ - `summarizers.make_step_table(..., from_data_point=)` (`summarizers.py`):
41
+ filters `datapoint_num >= from_data_point` then `group_by(test_id, cycle_num,
42
+ step_num, sub)`. Rebuilding from the spanning step's first datapoint
43
+ concatenates the kept partial raw + new rows into one step group. That is
44
+ why overlap works and why gap-append must use the same `from_data_point`.
45
+ - Issue #86 original algorithm: *"If r2(0) >= r1(last): find what step r2(0)
46
+ is in. That step and all the ones after will then belong to R2."* Gap-append
47
+ skipped that "find the step" part; this issue completes it.
48
+ - `tests/test_merge.py::test_update_gap_append_matches_full_recompute_oracle`:
49
+ cut is at `source_datapoint_num == 20` (`_single_test_raw` n_cycles=3) —
50
+ **step boundary** (start of cycle 3 step 1), so it does not catch this bug.
51
+ New tests go beside it; reuse `_single_test_raw`, `_process_raw`,
52
+ `_steps_oracle_equal`, `_summary_oracle_equal`.
53
+ - `step-c-rate-split.md`: `update_data` still appends `c_rate` to rebuilt rows
54
+ when kept steps carry it — keep that. Split steps are why downstream
55
+ `discharge_c_rate` drifts.
56
+ - Toolbox (`.issueflows/00-tools/`) empty. Graphify community 76 is the
57
+ `merge.py` helper cluster (`_trim_steps_for_overlap`, `update_data`); no
58
+ extra helper to reuse.
59
+
60
+ ## Approach
61
+
62
+ Implement the issue's primary expected behaviour (trim), not the
63
+ "document that callers must re-read" alternative.
64
+
65
+ 1. **Continuation trim on gap-append.** After detecting `gap_append`, still
66
+ keep **all** existing raw (`kept_raw = raw_pl` — new rows do not repeat the
67
+ partial step). Trim steps: if the first new row's `(cycle_num, step_num)`
68
+ equals the **last** existing step's `(cycle_num, step_num)`, drop that last
69
+ step and set `from_data_point` to its `datapoint_num_first`. Else keep all
70
+ steps and `from_data_point = min(new datapoint_num)` (current behaviour).
71
+
72
+ Implement as a **continuation fallback inside** `_trim_steps_for_overlap`
73
+ (when `overlap_idx is None`), then call that helper from the gap-append
74
+ branch with `new_pl` as the "incoming" frame. One trim function, two
75
+ callers. Do not add a second helper unless the fallback makes the overlap
76
+ docstring dishonest — if so, a tiny `_trim_steps_for_gap_append` next to it
77
+ is the fallback (same predicate).
78
+
79
+ Predicate (last step vs first incoming row, after sort by
80
+ `datapoint_num` / `datapoint_num_first`):
81
+
82
+ ```text
83
+ last[shdr.cycle_num] == first_new[nhdr.cycle_num]
84
+ and last[shdr.step_num] == first_new[nhdr.step_num]
85
+ ```
86
+
87
+ 2. **Do not trim `kept_raw` on gap.** Overlap drops existing rows with
88
+ `partition >= r2_start` because `new_raw` re-sends them. Gap does not.
89
+ `make_step_table(from_data_point=spanning_first)` then sees kept partial
90
+ points **plus** new points and rebuilds one step row.
91
+
92
+ 3. **Docs.** One sentence on `update_data` and on `_trim_steps_for_overlap`:
93
+ gap-append that continues the trailing `(cycle, step)` rebuilds that step
94
+ from `datapoint_num_first`, same as overlap.
95
+
96
+ Order: helper + `update_data` call site → tests → ruff → `HISTORY.md` at
97
+ close.
98
+
99
+ ## Files to touch
100
+
101
+ - `src/cellpycore/merge.py` — continuation fallback in
102
+ `_trim_steps_for_overlap` (or sibling helper); gap-append branch calls it
103
+ instead of keeping all steps; docstring.
104
+ - `tests/test_merge.py` — new oracle test(s) (see below).
105
+ - `HISTORY.md` — `[Unreleased]` fix bullet at close.
106
+
107
+ No `cell_core.py` change unless a docstring cross-ref is useful;
108
+ `update_core_data` already forwards to `update_data`.
109
+
110
+ ## Test strategy
111
+
112
+ Run: `uv run pytest` then `uv run ruff check && uv run ruff format --check`.
113
+
114
+ New tests in `tests/test_merge.py`, same helpers as the existing update
115
+ oracles:
116
+
117
+ - `test_update_gap_append_mid_step_matches_full_recompute_oracle` — the
118
+ issue. `_single_test_raw(..., n_cycles=2)`: cycle 2 step 1 is datapoints
119
+ 10–14. `base_raw` = `source_datapoint_num <= 12`, `extension` =
120
+ `source_datapoint_num >= 13` (`r2_start > r1_max` → gap). Assert
121
+ `_steps_oracle_equal` + `_summary_oracle_equal` vs `_process_raw(full_raw)`.
122
+ Also `updated.steps.height == oracle.steps.height` (no extra split row).
123
+ - `test_update_core_data_gap_append_mid_step_c_rate_matches_full` — same cut
124
+ through `CellpyCellCore.update_core_data` (default `refresh_derived=True`);
125
+ `discharge_c_rate` / `charge_c_rate` match a fully processed oracle
126
+ (`pytest.approx`). Guards the cellpy L6 symptom.
127
+
128
+ Existing `test_update_gap_append_matches_full_recompute_oracle` (step-boundary
129
+ gap) and `test_update_overlap_matches_full_recompute_oracle` stay green.
130
+ Optional e2e mid-step cut on the Arbin fixture is **out of scope** (unit
131
+ oracle is enough; fixture may be absent).
132
+
133
+ ## Open questions
134
+
135
+ - Trim vs document-only (issue lists both)? **Recommend trim** — overlap
136
+ already does this; #86's update algorithm already said "find what step
137
+ r2(0) is in". Document-only would leave core wrong and keep the cellpy
138
+ xfail forever.
139
+ - Extend `_trim_steps_for_overlap` vs a new `_trim_steps_for_gap_append`?
140
+ **Recommend extend** (continuation fallback when the first incoming
141
+ datapoint is past every `datapoint_num_last`). New helper if that muddies
142
+ the overlap-only name.
143
+ - Version bump at close: **patch** (behaviour bug, no API change). Cellpy
144
+ needs a new `cellpycore` release to drop its xfail.
@@ -0,0 +1,27 @@
1
+ # Issue #148 — status
2
+
3
+ - [x] Done
4
+
5
+ ## What's done
6
+
7
+ - Plan accepted (2026-09-08): trim trailing step on gap-append continuation;
8
+ rebuild from that step's `datapoint_num_first`.
9
+ - `_trim_steps_for_overlap`: continuation fallback when the first incoming
10
+ datapoint is past every existing step but `(cycle_num, step_num)` matches
11
+ the last kept step.
12
+ - `update_data` gap-append branch now calls `_trim_steps_for_overlap` on
13
+ `new_pl`; post-offset `from_data_point` still follows a non-continuation
14
+ gap.
15
+ - Tests: `test_update_gap_append_mid_step_matches_full_recompute_oracle`,
16
+ `test_update_core_data_gap_append_mid_step_c_rate_matches_full`.
17
+ - `uv run pytest`: 287 passed, 3 deselected. `ruff check` + `ruff format --check`
18
+ clean.
19
+ - Version bump `0.2.5` → `0.2.6`. `HISTORY.md` promoted (`#147`/`#148` under
20
+ `[0.2.6]`; `#142` backfilled under `[0.2.5]` to match tag `v0.2.5`).
21
+
22
+ ## Remaining work
23
+
24
+ - None in this repo.
25
+ - Follow-up (cellpy repo, after `cellpycore` pin bump to a release with this
26
+ fix): remove strict `xfail` on
27
+ `tests/test_incremental_update.py::test_gap_append_mid_step_equals_full_load`.
@@ -46,7 +46,7 @@ Reconciled against the actual repo state on **2026-06-26**. Status legend: ✅ d
46
46
  | STEP-09 Harmonize headers | ✅ done | `config.Cols` + spec tests; `legacy/mapping.py` gives lossless/total `config.Cols` ↔ legacy `Headers*` round-trip (`tests/test_header_mapping.py`) (#34/#35) |
47
47
  | STEP-10 Metadata scaffolding | ✅ done | `cellpycore.metadata` (`models.py`/`io.py`); `TestMeta`/`CellMeta`/`TestMetaCollection` + (de)serialize/merge; graceful-degradation guard (`tests/test_metadata.py`) (#37) |
48
48
  | STEP-11 Timestamp representation | ✅ done | `epoch_time_utc` + `first/last_epoch_time_utc` are int64-ns UTC; `cellpycore.timestamps` conversion helpers + fixture regenerated (`tests/test_timestamps.py`) (#32, PR #38) |
49
- | STEP-12 Unit-handling boundary | 🟡 partly done | `CellpyUnits` schema in `units/spec.py` (#40/#112) + `units.py` tooling behind the optional `units` extra; factors cross the seam by value; `cellpy` still keeps duplicate converters (delegation pending) |
49
+ | STEP-12 Unit-handling boundary | ✅ done | `CellpyUnits` schema in `units/spec.py` (#40/#112) + `units.py` tooling behind the optional `units` extra; factors cross the seam by value; `cellpy` now delegates `get_converter_to_specific` / `nominal_capacity_as_absolute` to `cellpycore.units` (jepegit/cellpy #451) — the duplicate converters are retired |
50
50
 
51
51
  Per-step `Status:` lines below repeat this for context; this table is the quick reference.
52
52
  The forward work beyond these twelve steps (STEP-13+) is enumerated in
@@ -266,12 +266,15 @@ Adopt the internal int64-ns timestamp representation.
266
266
 
267
267
  ## STEP-12 Unit-handling boundary (scaffolding/tooling in core; population & policy upstream)
268
268
 
269
- **Status:** 🟡 partly done — the `CellpyUnits` schema lives in `cellpycore.units.spec`
269
+ **Status:** ✅ done — the `CellpyUnits` schema lives in `cellpycore.units.spec`
270
270
  (#40, #112) and the pint-based conversion tooling (`cellpycore.units`:
271
271
  `get_converter_to_specific`, `nominal_capacity_as_absolute`, `Q`, output-unit defaults)
272
- already exist behind the optional `units` extra, attached to the `OldCellpyCellCore`
273
- bridge, and conversion factors already cross the seam **by value**. Remaining on the
274
- **cellpy** side only: duplicate converter functions not yet delegated to core.
272
+ exists behind the optional `units` extra, attached to the `OldCellpyCellCore`
273
+ bridge, and conversion factors cross the seam **by value**. The final cellpy-side move
274
+ has landed: `CellpyCell.get_converter_to_specific` / `nominal_capacity_as_absolute`
275
+ (and `convert_value` / `calculate_scaler`) now delegate to `cellpycore.units`
276
+ (jepegit/cellpy #451, unit plan Phase 2), guarded by converter-parity fixtures on both
277
+ sides — the duplicated converters are retired.
275
278
 
276
279
  **Codebase:** `cellpy-core` (scaffolding/tooling — `legacy.py` + `units.py`); population
277
280
  (`raw_units` from loaders) and the decision to delegate are `cellpy`'s opt-in.
@@ -299,8 +302,9 @@ already did for headers and the step engine).
299
302
 
300
303
  ## Remaining work (STEP-13+) — finalized 2026-06-28 (issue #39)
301
304
 
302
- The original twelve steps are complete except for the two intentionally-continuous /
303
- partly-done ones (STEP-06 golden fixtures, STEP-12 unit boundary). Going through the
305
+ The original twelve steps are complete except for the one intentionally-continuous
306
+ one (STEP-06 golden fixtures); STEP-12 (unit boundary) closed once the cellpy-side
307
+ converter delegation landed (jepegit/cellpy #451). Going through the
304
308
  roadmap and the companion design docs surfaced the remaining work below. Each item is now
305
309
  tracked as its own **cellpy-core** GitHub issue (the cellpy-side delegation work stays on
306
310
  jepegit/cellpy). Captured under issue #39.
@@ -339,12 +343,12 @@ flowchart TD
339
343
  S08 --> S09["STEP-09 Harmonize headers ✅"]
340
344
  S08 --> S10["STEP-10 Metadata scaffolding ✅"]
341
345
  S08 --> S11["STEP-11 Timestamp representation ✅"]
342
- S03 --> S12["STEP-12 Unit-handling boundary 🟡"]
346
+ S03 --> S12["STEP-12 Unit-handling boundary ✅"]
343
347
  S07["STEP-07 Build-backend swap ✅"] -.independent.-> S08
344
348
 
345
349
  classDef done fill:#d4edda,stroke:#28a745,color:#155724;
346
350
  classDef partly fill:#fff3cd,stroke:#ffc107,color:#856404;
347
351
  classDef todo fill:#f8d7da,stroke:#dc3545,color:#721c24;
348
- class S01,S02,S03,S04,S05,S07,S08,S09,S10,S11 done;
349
- class S06,S12 partly;
352
+ class S01,S02,S03,S04,S05,S07,S08,S09,S10,S11,S12 done;
353
+ class S06 partly;
350
354
  ```
@@ -0,0 +1,14 @@
1
+ {
2
+ "python.testing.pytestArgs": [
3
+ "tests"
4
+ ],
5
+ "python.testing.unittestEnabled": false,
6
+ "python.testing.pytestEnabled": true,
7
+ "python-envs.pythonProjects": [
8
+ {
9
+ "path": ".",
10
+ "envManager": "ms-python.python:venv",
11
+ "packageManager": "ms-python.python:pip"
12
+ }
13
+ ]
14
+ }
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.2.6] - 2026-09-08
8
+
9
+ - `update_core_data` treats an empty `new_raw` as a no-op (skips the derived refresh
10
+ instead of re-joining `*_c_rate` / `ir_*` columns as `*_right` duplicates);
11
+ `c_rates_to_summary` and `ir_to_summary` are now idempotent. (#147)
12
+ - `update_data` gap-append that continues the trailing `(cycle, step)` now
13
+ rebuilds that step from `datapoint_num_first` instead of splitting it into
14
+ two partial rows (summary C-rate matches a full load). (#148)
15
+
16
+ ## [0.2.5] - 2026-09-08
17
+
18
+ - Harden `cycle_mode` unwrapping in `OldCellpyCellCore` / `_cycle_mode_to_test_mode`. (#142)
19
+
7
20
  ## [0.2.4] - 2026-07-23
8
21
 
9
22
  - Add Equivalent Full Cycles (EFC) utilities (#138). `summarizers.throughput_to_raw`
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: cellpycore
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Core engine for cellpy: fast, thread-safe step and cycle summarization of battery-cycling raw data
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cellpycore"
3
- version = "0.2.4"
3
+ version = "0.2.6"
4
4
  license = "MIT"
5
5
  license-files = ["LICEN[CS]E*"]
6
6
  description = "Core engine for cellpy: fast, thread-safe step and cycle summarization of battery-cycling raw data"