flowbook-python 0.1.0__tar.gz → 0.1.2__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 (422) hide show
  1. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/FORMAL_DEVELOPMENT.md +26 -7
  2. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/PKG-INFO +5 -2
  3. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/README.md +4 -1
  4. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/_version.py +1 -1
  5. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/flowbook_kernel.py +102 -18
  6. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/notebook_state.py +22 -0
  7. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/protocol.py +8 -2
  8. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/reproducibility_enforcer.py +25 -0
  9. flowbook_python-0.1.2/flowbook/kernel/tests/test_meaningful_edit.py +243 -0
  10. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/package.json +2 -2
  11. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/schemas/flowbook/package.json.orig +1 -1
  12. flowbook_python-0.1.0/flowbook/labextension/static/873.3ca7ae352f965bccc339.js → flowbook_python-0.1.2/flowbook/labextension/static/873.3edec525c5c79ec55cd6.js +1 -1
  13. flowbook_python-0.1.0/flowbook/labextension/static/remoteEntry.97e370ce33befeb5451b.js → flowbook_python-0.1.2/flowbook/labextension/static/remoteEntry.9c89c62fdbe6db112e2d.js +1 -1
  14. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/session.py +35 -11
  15. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/package.json +1 -1
  16. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/executionhook.ts +8 -5
  17. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/protocol.ts +3 -0
  18. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/agents/reproducibility-fixer.md +0 -0
  19. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/basic-run-nbi.md +0 -0
  20. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/basic-run.md +0 -0
  21. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/categorize-repro-errors.md +0 -0
  22. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/fix-notebook.md +0 -0
  23. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/sync-spec.md +0 -0
  24. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.copier-answers.yml +0 -0
  25. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.gitignore +0 -0
  26. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.prettierignore +0 -0
  27. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.yarnrc.yml +0 -0
  28. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/CLAUDE.md +0 -0
  29. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/CLI.md +0 -0
  30. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/CONFLICT_RELATION.md +0 -0
  31. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/LICENSE +0 -0
  32. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/MCP_ARCHITECTURE.md +0 -0
  33. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/FlowBookTutorial.ipynb +0 -0
  34. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/GettingStarted.ipynb +0 -0
  35. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/cudf/CudfCheckpointBenchmark.ipynb +0 -0
  36. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/cudf/CudfGpuCheckpoint.ipynb +0 -0
  37. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/cudf/CudfGpuPipeline.ipynb +0 -0
  38. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/01_Basic_Tracking.ipynb +0 -0
  39. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/02_Wrong_Order.ipynb +0 -0
  40. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/03_Rejected_Overwrite.ipynb +0 -0
  41. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/04_Column_Independence.ipynb +0 -0
  42. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/05_Column_Conflict.ipynb +0 -0
  43. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/06_Edit_Detection.ipynb +0 -0
  44. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/09_UnrecoverableMutation_Lists.ipynb +0 -0
  45. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/10_UnrecoverableMutation_Arrays.ipynb +0 -0
  46. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/11_UnrecoverableMutation_DataFrameColumns.ipynb +0 -0
  47. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/forecasting-sticker-sales-1st-place-solution-gkhoussa/forecasting-sticker-sales-1st-place-solution.ipynb +0 -0
  48. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/00_NoReadAndWrite.ipynb +0 -0
  49. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/01_WriteBeforeRead.ipynb +0 -0
  50. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/02_NoReadBeforeWrite.ipynb +0 -0
  51. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/03_NoWriteAfterRead.ipynb +0 -0
  52. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/04_ForwardStale.ipynb +0 -0
  53. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/05_BackwardStale.ipynb +0 -0
  54. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/__init__.py +0 -0
  55. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/__main__.py +0 -0
  56. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/__init__.py +0 -0
  57. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/__main__.py +0 -0
  58. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/baseline_kernel.py +0 -0
  59. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/kernelspec/kernel.json +0 -0
  60. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/__init__.py +0 -0
  61. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/__main__.py +0 -0
  62. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/checkpoint_client.py +0 -0
  63. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/checkpoint_kernel.py +0 -0
  64. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/kernelspec/kernel.json +0 -0
  65. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/__init__.py +0 -0
  66. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/cli.py +0 -0
  67. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/compare_fixed_overhead.py +0 -0
  68. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/compare_overhead.py +0 -0
  69. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/flowbook_timers.py +0 -0
  70. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/helpers.py +0 -0
  71. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/metadata_cli.py +0 -0
  72. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/models.py +0 -0
  73. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/optimization_metadata.py +0 -0
  74. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/plot_extraction.py +0 -0
  75. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/plot_rendering.py +0 -0
  76. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/show_errors.py +0 -0
  77. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/slurm_logs.py +0 -0
  78. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/stats_cli.py +0 -0
  79. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/stats_display.py +0 -0
  80. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/__init__.py +0 -0
  81. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_flowbook_timers.py +0 -0
  82. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_metadata_format.py +0 -0
  83. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_models.py +0 -0
  84. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_models_v5.py +0 -0
  85. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_plot_extraction.py +0 -0
  86. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_plot_extraction_v5.py +0 -0
  87. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_plot_rendering.py +0 -0
  88. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_rerun_overhead_plots.py +0 -0
  89. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/handlers.py +0 -0
  90. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/__init__.py +0 -0
  91. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/__main__.py +0 -0
  92. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/access_events.py +0 -0
  93. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/change_detector.py +0 -0
  94. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/changes.py +0 -0
  95. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/flowbook_client.py +0 -0
  96. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/kernelspec/kernel.json +0 -0
  97. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/kernelspec/logo-32x32.png +0 -0
  98. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/kernelspec/logo-64x64.png +0 -0
  99. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/loc_ids.py +0 -0
  100. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/locations.py +0 -0
  101. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/models.py +0 -0
  102. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/LITMUS_TESTS.yaml +0 -0
  103. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/__init__.py +0 -0
  104. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/conftest.py +0 -0
  105. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/generate_litmus_latex.py +0 -0
  106. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/litmus_helpers.py +0 -0
  107. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/litmus_output/litmus_tests.tex +0 -0
  108. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/litmus_output/litmus_tests.txt +0 -0
  109. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_alias_conflicts.py +0 -0
  110. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_change_detector_locs.py +0 -0
  111. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_column_staleness_bug.py +0 -0
  112. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_converters.py +0 -0
  113. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_delete_transitions.py +0 -0
  114. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_flowbook_kernel.py +0 -0
  115. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_forward_dependency.py +0 -0
  116. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_implementation_theory_diff.py +0 -0
  117. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_inplace_mutation_detection.py +0 -0
  118. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_litmus.py +0 -0
  119. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_loc_ids.py +0 -0
  120. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_locations.py +0 -0
  121. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_locset_integration.py +0 -0
  122. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_measure_rerun_overhead.py +0 -0
  123. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_notebook_state.py +0 -0
  124. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_optimizations.py +0 -0
  125. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_order_changes.py +0 -0
  126. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_order_changes_complex.py +0 -0
  127. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_paper_examples.py +0 -0
  128. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_protocol.py +0 -0
  129. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_provenance.py +0 -0
  130. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_removed_writes_scenario.py +0 -0
  131. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_reproducibility_enforcer.py +0 -0
  132. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_reproducibility_structural.py +0 -0
  133. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_typed_predicates.py +0 -0
  134. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_uncopyable_as_write.py +0 -0
  135. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_var_binding_semantics.py +0 -0
  136. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_discovery.py +0 -0
  137. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/__init__.py +0 -0
  138. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/ast_utils.py +0 -0
  139. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/base_client.py +0 -0
  140. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/base_kernel.py +0 -0
  141. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/checkpoint.py +0 -0
  142. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/checkpoint.py.backup +0 -0
  143. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/column_provenance.py +0 -0
  144. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/column_tracking.py +0 -0
  145. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/cudf_compat.py +0 -0
  146. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/deepcopy.py +0 -0
  147. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/deepcopyable.py +0 -0
  148. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/df_subset_detector.py +0 -0
  149. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/diff.py +0 -0
  150. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/display_helpers.py +0 -0
  151. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/experimental_client.py +0 -0
  152. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/extended_types.py +0 -0
  153. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/file_checkpoint.py +0 -0
  154. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/fs_magics.py +0 -0
  155. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/heap_size.py +0 -0
  156. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/install.py +0 -0
  157. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/json_utils.py +0 -0
  158. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/kernelspec/kernel.json +0 -0
  159. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/kernelspec/logo-64x64.png +0 -0
  160. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/locals.py +0 -0
  161. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/memory_checkpoint.py +0 -0
  162. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/models.py +0 -0
  163. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/opaque.py +0 -0
  164. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/process_cleanup.py +0 -0
  165. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/structural_tracking.py +0 -0
  166. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/test_diff.py.backup +0 -0
  167. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/test_subset_perf.py +0 -0
  168. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/__init__.py +0 -0
  169. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_ast_utils.py +0 -0
  170. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_base_kernel.py +0 -0
  171. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_catboost_pool.py +0 -0
  172. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_chained_assignment_error.py +0 -0
  173. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint.py +0 -0
  174. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_comprehensive.py +0 -0
  175. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_corner_cases.py +0 -0
  176. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_df_subsets.py +0 -0
  177. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_functions.py +0 -0
  178. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_memory_sharing.py +0 -0
  179. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_nested.py +0 -0
  180. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_object_conversion.py +0 -0
  181. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_overhead_measurement.py +0 -0
  182. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_provenance.py +0 -0
  183. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking.py +0 -0
  184. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking_methods.py +0 -0
  185. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking_performance.py +0 -0
  186. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking_setitem_reads.py +0 -0
  187. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_checkpoint_dedup.py +0 -0
  188. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_checkpoint_inflation.py +0 -0
  189. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_checkpoint_perf.py +0 -0
  190. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_compat.py +0 -0
  191. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_proxy_fingerprint.py +0 -0
  192. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_proxy_tracking.py +0 -0
  193. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_deep_alias_detection.py +0 -0
  194. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_deepcopy_preserve_mode.py +0 -0
  195. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_deepcopyable.py +0 -0
  196. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_df_subset_detector.py +0 -0
  197. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_byte_comparison.py +0 -0
  198. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_deep.py +0 -0
  199. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_float_tolerance.py +0 -0
  200. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_keras_catboost.py +0 -0
  201. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_object_float.py +0 -0
  202. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_structural.py +0 -0
  203. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_display_helpers.py +0 -0
  204. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_extended_types_coverage.py +0 -0
  205. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_extension_dtypes.py +0 -0
  206. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_file_checkpoint.py +0 -0
  207. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_file_checkpoint_comprehensive.py +0 -0
  208. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_fs_magics.py +0 -0
  209. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_gpu_checkpoint.py +0 -0
  210. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_gpu_checkpoint_perf.py +0 -0
  211. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_heap_size.py +0 -0
  212. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_incremental_checkpoint.py +0 -0
  213. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_index_deepcopy.py +0 -0
  214. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_install.py +0 -0
  215. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_json_utils.py +0 -0
  216. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_keras_deepcopy.py +0 -0
  217. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_large_list_cache.py +0 -0
  218. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_lightgbm_checkpoint.py +0 -0
  219. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_locals.py +0 -0
  220. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_checkpoint.py +0 -0
  221. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_checkpoint_comprehensive.py +0 -0
  222. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_checkpoint_edge_cases.py +0 -0
  223. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_snapshot.py +0 -0
  224. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_models.py +0 -0
  225. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_models_coverage.py +0 -0
  226. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_multiindex.py +0 -0
  227. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_keras.py +0 -0
  228. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_keras_gaps.py +0 -0
  229. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_ml.py +0 -0
  230. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_pytorch.py +0 -0
  231. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_pytorch_gaps.py +0 -0
  232. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_pytorch_diff.py +0 -0
  233. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_shap_checkpoint.py +0 -0
  234. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_stacking_checkpoint.py +0 -0
  235. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_structural_tracking.py +0 -0
  236. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_targetencoder_checkpoint.py +0 -0
  237. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_timeout_handler.py +0 -0
  238. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking.py +0 -0
  239. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking_coverage.py +0 -0
  240. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking_performance.py +0 -0
  241. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking_structural.py +0 -0
  242. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_types_coverage.py +0 -0
  243. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_virtual_fs.py +0 -0
  244. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/timeout_handler.py +0 -0
  245. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tracking.py +0 -0
  246. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/types.py +0 -0
  247. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/virtual_fs.py +0 -0
  248. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/schemas/flowbook/plugin.json +0 -0
  249. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/728.b1df4bca1a3305d0d0a7.js +0 -0
  250. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/905.94c2bfb401597cc2a103.js +0 -0
  251. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/905.94c2bfb401597cc2a103.js.LICENSE.txt +0 -0
  252. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/951.ba84389925d6a0676e79.js +0 -0
  253. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/style.js +0 -0
  254. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/third-party-licenses.json +0 -0
  255. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/__init__.py +0 -0
  256. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/jupyter_config.py +0 -0
  257. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/server.py +0 -0
  258. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/__init__.py +0 -0
  259. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_cell_validation.py +0 -0
  260. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_contents_api.py +0 -0
  261. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_jupyter_config.py +0 -0
  262. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_kernel_discovery.py +0 -0
  263. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_new_tools.py +0 -0
  264. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_session_integration.py +0 -0
  265. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/MANUAL_UI_TESTS.md +0 -0
  266. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/__init__.py +0 -0
  267. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/claude_commands/flowbook-nb-fix.md +0 -0
  268. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/extension.py +0 -0
  269. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/extension_data/extension.json +0 -0
  270. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/session.py +0 -0
  271. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/__init__.py +0 -0
  272. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/test_extension.py +0 -0
  273. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/test_session.py +0 -0
  274. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/test_tools.py +0 -0
  275. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tools.py +0 -0
  276. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/__init__.py +0 -0
  277. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/fix_repro_errors.py +0 -0
  278. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/parse_repro_errors.py +0 -0
  279. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/tests/__init__.py +0 -0
  280. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/tests/test_fix_repro_errors.py +0 -0
  281. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/__init__.py +0 -0
  282. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/base.py +0 -0
  283. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/comm_models.py +0 -0
  284. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/__init__.py +0 -0
  285. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/compare_baseline.py +0 -0
  286. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/execute.py +0 -0
  287. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/execute_base.py +0 -0
  288. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/__init__.py +0 -0
  289. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_compare_baseline_rerun.py +0 -0
  290. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_compare_baseline_v3.py +0 -0
  291. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_downsample_csv.py +0 -0
  292. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_flowbook_update_protocol.py +0 -0
  293. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_gpu_memory_recording.py +0 -0
  294. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_path_traversal.py +0 -0
  295. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/handlers.py +0 -0
  296. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/kernel_helper.py +0 -0
  297. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/kernel_manager.py +0 -0
  298. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/registry.py +0 -0
  299. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/tests/__init__.py +0 -0
  300. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/tests/test_handlers.py +0 -0
  301. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/tests/test_kernel_manager.py +0 -0
  302. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/slurm/slurm_cli.py +0 -0
  303. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/README.md +0 -0
  304. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/__init__.py +0 -0
  305. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/benchmark_checkpoint.py +0 -0
  306. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/checkpoint_overhead_test.py +0 -0
  307. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/correctness.py +0 -0
  308. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/kernel_comparison.py +0 -0
  309. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebook_loader.py +0 -0
  310. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/data_pipeline.ipynb +0 -0
  311. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/dependencies.ipynb +0 -0
  312. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/deterministic.ipynb +0 -0
  313. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/ml_workflow.ipynb +0 -0
  314. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/multi_dataframe.ipynb +0 -0
  315. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/nondeterministic.ipynb +0 -0
  316. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/pandas_heavy.ipynb +0 -0
  317. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/performance.py +0 -0
  318. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/plot_checkpoint_percentage.py +0 -0
  319. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/plot_checkpoint_timings.py +0 -0
  320. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/results.py +0 -0
  321. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/runner.py +0 -0
  322. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/__init__.py +0 -0
  323. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/run_correctness.py +0 -0
  324. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/run_kernel_comparison.py +0 -0
  325. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/run_performance.py +0 -0
  326. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/cell_ids.py +0 -0
  327. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/cell_index.py +0 -0
  328. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/dependencies.py +0 -0
  329. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/flowbook_metadata.py +0 -0
  330. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/gpu_memory.py +0 -0
  331. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/kernel_installer.py +0 -0
  332. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/liveness.py +0 -0
  333. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/metadata_extractor.py +0 -0
  334. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/model_copy.py +0 -0
  335. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/nb_diff.py +0 -0
  336. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/notebook_analysis.py +0 -0
  337. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/notebook_to_python.py +0 -0
  338. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/output.py +0 -0
  339. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/prompts.py +0 -0
  340. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/prompts.yaml +0 -0
  341. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/__init__.py +0 -0
  342. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_cell_ids.py +0 -0
  343. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_cell_index.py +0 -0
  344. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_dependencies.py +0 -0
  345. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_gpu_memory.py +0 -0
  346. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_liveness.py +0 -0
  347. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_notebook_analysis.py +0 -0
  348. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_notebook_to_python.py +0 -0
  349. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/text.py +0 -0
  350. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/install.json +0 -0
  351. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/jupyter-config/server-config/flowbook.json +0 -0
  352. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/media/flowbook-small.png +0 -0
  353. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/media/flowbook.png +0 -0
  354. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/pyproject.toml +0 -0
  355. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/schema/plugin.json +0 -0
  356. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/run-base.sh +0 -0
  357. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/run-flowbook.sh +0 -0
  358. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/timers.sh +0 -0
  359. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/type_coverage.py +0 -0
  360. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/setup.py +0 -0
  361. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/api.ts +0 -0
  362. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/executiondialog.tsx +0 -0
  363. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/cellhighlighter.ts +0 -0
  364. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/celltoolbar.ts +0 -0
  365. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/executionhook.ts +0 -0
  366. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/history.ts +0 -0
  367. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/historypanel.tsx +0 -0
  368. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/manager.ts +0 -0
  369. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/metadatapanel.tsx +0 -0
  370. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/plugin.ts +0 -0
  371. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/toolbar.ts +0 -0
  372. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/types.ts +0 -0
  373. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/unittestpanel.tsx +0 -0
  374. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/unittesttracker.ts +0 -0
  375. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/kernel.ts +0 -0
  376. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/logpanel.tsx +0 -0
  377. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/messagecomponents.tsx +0 -0
  378. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/cellindex.ts +0 -0
  379. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/cellindexutils.ts +0 -0
  380. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/cellhighlighter.ts +0 -0
  381. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/dependenciespanel.tsx +0 -0
  382. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/metadatapanel.tsx +0 -0
  383. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/nbibridge.ts +0 -0
  384. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/plugin.ts +0 -0
  385. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/stalenessmanager.ts +0 -0
  386. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/stalenessnotice.ts +0 -0
  387. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/toolbar.ts +0 -0
  388. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/types.ts +0 -0
  389. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/violationnotice.ts +0 -0
  390. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/handler.ts +0 -0
  391. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/index.ts +0 -0
  392. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/shared/kerneldetection.ts +0 -0
  393. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/shared/types.ts +0 -0
  394. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/_archived.css +0 -0
  395. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/base.css +0 -0
  396. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/dependencies.css +0 -0
  397. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/index.css +0 -0
  398. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/index.js +0 -0
  399. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/demo_diffresult_basemodel.py +0 -0
  400. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/demo_markdown.py +0 -0
  401. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/demo_test_comm_structure.py +0 -0
  402. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_comm_simple.py +0 -0
  403. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_diff_refactor.py +0 -0
  404. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_diffresult_serialization.py +0 -0
  405. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_dtype_compatibility.py +0 -0
  406. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_env_in_optimize.py +0 -0
  407. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_execution_error_capture.py +0 -0
  408. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_generate_with_env.py +0 -0
  409. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_markdown_format.py +0 -0
  410. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_notebook.ipynb +0 -0
  411. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_command.py +0 -0
  412. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_fixes.py +0 -0
  413. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_keys_to_include.py +0 -0
  414. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_validation.py +0 -0
  415. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_partial_execution_bug.py +0 -0
  416. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_progress_messages.py +0 -0
  417. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_prompt_integration.py +0 -0
  418. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_real_kernel_message_capture.py +0 -0
  419. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_test_comm.py +0 -0
  420. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_validate_change_command.py +0 -0
  421. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tsconfig.json +0 -0
  422. {flowbook_python-0.1.0 → flowbook_python-0.1.2}/yarn.lock +0 -0
@@ -219,6 +219,25 @@ S →^{Edit(i, c)} S'
219
219
  S · (T, R, W) ⇒^{Edit(i, c)} S' · (T[i := STALE], R, W)
220
220
  ```
221
221
 
222
+ **Meaningful-edit refinement (implementation).** The instrumentation classifies an
223
+ edit by comparing the new source against a per-cell **AST fingerprint** Fᵢ captured
224
+ on the last [Inst-Run]. Let `fp(c)` be the canonical AST of the edited source `c`
225
+ (IPython input transform → `ast.parse` → `ast.dump`; `⊥` if unparseable):
226
+
227
+ ```
228
+ = T[i := STALE] if fp(c) ≠ Fᵢ or fp(c) = ⊥ (meaningful / invalid)
229
+ T' = clear CODE_CHANGED at i if fp(c) = Fᵢ (cosmetic / reverted)
230
+ ```
231
+
232
+ `fp` discards comments, blank lines, indentation, and source positions, so
233
+ whitespace- and comment-only edits leave `T` unchanged. When `fp(c) = Fᵢ` the edit
234
+ is semantically a no-op (or a round-trip back to the last-run source), so the
235
+ `CODE_CHANGED` reason is cleared — the cell returns to CLEAN unless it is stale for
236
+ another reason (e.g. `FORWARD_STALE`). Fingerprints are stored in
237
+ `NotebookState.fingerprints`; `fp` is computed by `FlowbookKernel._source_fingerprint`
238
+ (it owns source/transformer access); the clear path is
239
+ `ReproducibilityEnforcer.clear_code_changed` → `NotebookState.clear_pre_execution_reasons`.
240
+
222
241
  **[Inst-Run]**
223
242
 
224
243
  ```
@@ -401,13 +420,13 @@ Validity predicates are implemented inline within `check()`, following the [Inst
401
420
 
402
421
  The `check()` method implements [Inst-Run] exactly, with formal citations in comments:
403
422
 
404
- | main.tex | FORMAL_DEVELOPMENT.md | Code |
405
- | ----------------- | --------------------- | ------------------------------------------------------------- |
406
- | Inst-Edit | §3.4 [Inst-Edit] | `mark_cell_edited()` in `kernel/reproducibility_enforcer.py` |
407
- | Inst-Run | §3.4 [Inst-Run] | `check()` in `kernel/reproducibility_enforcer.py` |
408
- | Inst-Insert | §3.5 [Inst-Insert] | `set_cell_order()` detecting new cells |
409
- | Inst-Delete | §3.5 [Inst-Delete] | `_handle_deletions()` in `kernel/reproducibility_enforcer.py` |
410
- | Inst-Move-Down/Up | §3.5 [Inst-Move-*] | `_handle_moves()` in `kernel/reproducibility_enforcer.py` |
423
+ | main.tex | FORMAL_DEVELOPMENT.md | Code |
424
+ | ----------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
425
+ | Inst-Edit | §3.4 [Inst-Edit] | `mark_cell_edited()` (meaningful) / `clear_code_changed()` (cosmetic/revert) in `kernel/reproducibility_enforcer.py`; classified by `_process_cell_edit()` + `_source_fingerprint()` in `kernel/flowbook_kernel.py` against `NotebookState.fingerprints` |
426
+ | Inst-Run | §3.4 [Inst-Run] | `check()` in `kernel/reproducibility_enforcer.py` |
427
+ | Inst-Insert | §3.5 [Inst-Insert] | `set_cell_order()` detecting new cells |
428
+ | Inst-Delete | §3.5 [Inst-Delete] | `_handle_deletions()` in `kernel/reproducibility_enforcer.py` |
429
+ | Inst-Move-Down/Up | §3.5 [Inst-Move-*] | `_handle_moves()` in `kernel/reproducibility_enforcer.py` |
411
430
 
412
431
  **[Inst-Run] Implementation Structure:**
413
432
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowbook-python
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Reproducibility enforcement for Jupyter notebooks: a JupyterLab extension that enforces rerun consistency by tracking variable- and column-level dependencies between cells.
5
5
  Project-URL: Homepage, https://github.com/stephenfreund/FlowBook
6
6
  Project-URL: Bug Tracker, https://github.com/stephenfreund/FlowBook/issues
@@ -97,7 +97,7 @@ Description-Content-Type: text/markdown
97
97
  **Reproducibility enforcement for Jupyter notebooks.**
98
98
 
99
99
  FlowBook is a JupyterLab extension that enforces _rerun consistency_:
100
- re-executing each cell from the current state would produce a result
100
+ re-executing any cell from the current state would produce a result
101
101
  consistent with a top-to-bottom execution of the notebook,
102
102
  regardless of which cells have been run, modified, and rerun.
103
103
  Cells whose inputs may have changed are marked _stale_,
@@ -108,6 +108,9 @@ When every cell is _clean_ — executed and rerun consistent —
108
108
  the notebook is guaranteed reproducible: running it top-to-bottom from
109
109
  an empty store yields exactly the outputs currently recorded.
110
110
 
111
+ For technical details and a complete evaluation, see our arXiv paper: [FlowBook: Enforcing Reproducibility in Computational Notebooks
112
+ ](https://arxiv.org/abs/2605.01560).
113
+
111
114
  ## Quick Start
112
115
 
113
116
  Install FlowBook using `pip`:
@@ -10,7 +10,7 @@
10
10
  **Reproducibility enforcement for Jupyter notebooks.**
11
11
 
12
12
  FlowBook is a JupyterLab extension that enforces _rerun consistency_:
13
- re-executing each cell from the current state would produce a result
13
+ re-executing any cell from the current state would produce a result
14
14
  consistent with a top-to-bottom execution of the notebook,
15
15
  regardless of which cells have been run, modified, and rerun.
16
16
  Cells whose inputs may have changed are marked _stale_,
@@ -21,6 +21,9 @@ When every cell is _clean_ — executed and rerun consistent —
21
21
  the notebook is guaranteed reproducible: running it top-to-bottom from
22
22
  an empty store yields exactly the outputs currently recorded.
23
23
 
24
+ For technical details and a complete evaluation, see our arXiv paper: [FlowBook: Enforcing Reproducibility in Computational Notebooks
25
+ ](https://arxiv.org/abs/2605.01560).
26
+
24
27
  ## Quick Start
25
28
 
26
29
  Install FlowBook using `pip`:
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '0.1.0'
4
+ __version__ = VERSION = '0.1.2'
@@ -342,6 +342,7 @@ See checkpoint.py sections 13-14 for implementation details.
342
342
  ================================================================================
343
343
  """
344
344
 
345
+ import ast
345
346
  import os
346
347
  import re
347
348
  import time
@@ -525,7 +526,7 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
525
526
  if msg_type == "notebook_structure":
526
527
  self._process_structure_update(msg["cell_order"])
527
528
  elif msg_type == "cell_edited":
528
- self._process_cell_edit(msg["cell_id"])
529
+ self._process_cell_edit(msg["cell_id"], msg.get("source"))
529
530
  elif msg_type == "continue_after_violation":
530
531
  self._set_continue_after_violation(msg["enabled"])
531
532
  elif msg_type == "sync":
@@ -579,26 +580,94 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
579
580
  log(f"[structure_update] ERROR: {e}")
580
581
  log(f"[structure_update] Traceback: {traceback.format_exc()}")
581
582
 
582
- def _process_cell_edit(self, cell_id: str) -> None:
583
- """Process a cell_edited command. Marks the cell stale ([Inst-Edit])."""
583
+ def _source_fingerprint(self, source: str) -> Optional[str]:
584
+ """Canonical AST fingerprint of a cell's source ([Inst-Edit]).
585
+
586
+ Runs the source through IPython's input transformer so magics/`!`
587
+ commands become valid Python, then returns ``ast.dump`` of the parsed
588
+ tree. This is insensitive to comments, blank lines, indentation, and
589
+ source positions, so cosmetic edits produce an identical fingerprint.
590
+ Returns None when the (possibly partial) source cannot be parsed.
591
+ """
592
+ try:
593
+ transformed = self.shell.input_transformer_manager.transform_cell(source)
594
+ return ast.dump(ast.parse(transformed))
595
+ except (SyntaxError, ValueError):
596
+ return None
597
+
598
+ def _send_cell_edit_metadata(self, cell_id: str, stale_cells: list, icon: str, text: str) -> None:
599
+ """Emit metadata + status reflecting a cell's post-edit staleness."""
600
+ staleness_reasons = self._enforcer._notebook_state.get_all_reasons()
601
+ metadata = ReproducibilityMetadata(
602
+ cell_id=cell_id,
603
+ execution_seq=self._enforcer.seq_counter,
604
+ read_locs=[],
605
+ write_locs=[],
606
+ changed_locs=[],
607
+ stale_cells=stale_cells,
608
+ cell_order=self._enforcer.cell_order,
609
+ staleness_reasons=staleness_reasons,
610
+ )
611
+ self._send_flowbook_message(build_metadata_message(metadata))
612
+ self._send_flowbook_message(build_status_message(icon, text, cell_id=cell_id))
613
+
614
+ def _process_cell_edit(self, cell_id: str, source: Optional[str] = None) -> None:
615
+ """Process a cell_edited command ([Inst-Edit]).
616
+
617
+ Compares the edited source's AST fingerprint against the one captured when
618
+ the cell last executed. If they match, the edit is cosmetic
619
+ (whitespace/comments) or a round-trip back to the last-run source, so
620
+ CODE_CHANGED is cleared (the cell returns to clean unless stale for another
621
+ reason). Otherwise — including unparseable or sourceless edits — the cell is
622
+ marked stale, preserving the conservative legacy behavior.
623
+ """
584
624
  if not cell_id:
585
625
  return
586
- stale_cells = self._enforcer.mark_cell_edited(cell_id)
587
- if cell_id in stale_cells:
588
- staleness_reasons = self._enforcer._notebook_state.get_all_reasons()
589
- metadata = ReproducibilityMetadata(
590
- cell_id=cell_id,
591
- execution_seq=self._enforcer.seq_counter,
592
- read_locs=[],
593
- write_locs=[],
594
- changed_locs=[],
595
- stale_cells=stale_cells,
596
- cell_order=self._enforcer.cell_order,
597
- staleness_reasons=staleness_reasons,
626
+
627
+ new_fp = self._source_fingerprint(source) if source is not None else None
628
+ old_fp = self._enforcer.get_fingerprint(cell_id)
629
+
630
+ def _fp_repr(fp: Optional[str]) -> str:
631
+ if fp is None:
632
+ return "None"
633
+ return f"<{len(fp)}c #{hash(fp) & 0xffffffff:08x}>"
634
+
635
+ log(
636
+ f"[Inst-Edit] cell={cell_id} source_present={source is not None} "
637
+ f"new_fp={_fp_repr(new_fp)} old_fp={_fp_repr(old_fp)} "
638
+ f"match={new_fp is not None and old_fp is not None and new_fp == old_fp}"
639
+ )
640
+
641
+ if new_fp is not None and old_fp is not None and new_fp == old_fp:
642
+ # Source semantically matches the last execution — not stale due to code.
643
+ stale_before = set(self._enforcer.get_stale_cells())
644
+ stale_cells = self._enforcer.clear_code_changed(cell_id)
645
+ changed = set(stale_cells) != stale_before
646
+ log(
647
+ f"[Inst-Edit] cell={cell_id} DECISION=cosmetic/revert -> clear_code_changed "
648
+ f"(status_changed={changed}, stale_now={stale_cells})"
598
649
  )
599
- self._send_flowbook_message(build_metadata_message(metadata))
600
- self._send_flowbook_message(
601
- build_status_message("✏️", "Cell edited, marked stale", cell_id=cell_id)
650
+ if changed:
651
+ self._send_cell_edit_metadata(
652
+ cell_id, stale_cells, "↩️", "Edit reverted to last run, cleared"
653
+ )
654
+ return
655
+
656
+ if source is not None and new_fp is None:
657
+ reason = "unparseable"
658
+ elif source is None:
659
+ reason = "no-source"
660
+ else:
661
+ reason = "ast-differs"
662
+ stale_cells = self._enforcer.mark_cell_edited(cell_id)
663
+ marked = cell_id in stale_cells
664
+ log(
665
+ f"[Inst-Edit] cell={cell_id} DECISION=meaningful ({reason}) -> mark_cell_edited "
666
+ f"(marked_stale={marked}, stale_now={stale_cells})"
667
+ )
668
+ if marked:
669
+ self._send_cell_edit_metadata(
670
+ cell_id, stale_cells, "✏️", "Cell edited, marked stale"
602
671
  )
603
672
 
604
673
  def _set_continue_after_violation(self, enabled: bool) -> None:
@@ -1263,6 +1332,10 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
1263
1332
  if cell_meta and "cell_order" in cell_meta:
1264
1333
  self._enforcer.set_cell_order(cell_meta["cell_order"])
1265
1334
 
1335
+ # Capture the raw source (before magic stripping) for the [Inst-Edit]
1336
+ # fingerprint, so it matches the source the frontend/MCP send on edit.
1337
+ original_code = code
1338
+
1266
1339
  # Check for notebook_structure magic (parse and remove if present)
1267
1340
  code = self._process_structure_magic(code)
1268
1341
 
@@ -1470,6 +1543,17 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
1470
1543
  error(f"Reproducibility truncation: {err.message}")
1471
1544
  self._send_truncation_details(err.detail["truncation_details"])
1472
1545
 
1546
+ # [Inst-Edit] baseline: record the fingerprint of the source that
1547
+ # just committed, so future edits can be classified as meaningful
1548
+ # or cosmetic. Reached only on the committed path (rejected
1549
+ # executions return above).
1550
+ _baseline_fp = self._source_fingerprint(original_code)
1551
+ self._enforcer.set_fingerprint(self._cell_id, _baseline_fp)
1552
+ log(
1553
+ f"[Inst-Edit] cell={self._cell_id} stored baseline fingerprint "
1554
+ f"{'None' if _baseline_fp is None else f'<{len(_baseline_fp)}c #{hash(_baseline_fp) & 0xffffffff:08x}>'}"
1555
+ )
1556
+
1473
1557
  # Display results (no longer skip on violations since we don't reject)
1474
1558
  skip_display = False
1475
1559
  if (
@@ -75,6 +75,11 @@ class NotebookState:
75
75
  execution_seq: Dict[str, int] = field(default_factory=dict) # cell_id -> seq number
76
76
  structural_reads_values: Dict[str, Dict[str, Dict[str, str]]] = field(default_factory=dict) # cell_id -> var -> attr -> value
77
77
  typed_changes: Dict[str, List["Change"]] = field(default_factory=dict) # cell_id -> changes
78
+ # Canonical AST fingerprint of the source last executed for each cell.
79
+ # Used by [Inst-Edit] to decide whether a source edit is meaningful: an edit
80
+ # whose fingerprint matches this baseline is treated as cosmetic (clears
81
+ # CODE_CHANGED) rather than marking the cell stale.
82
+ fingerprints: Dict[str, str] = field(default_factory=dict) # cell_id -> AST fingerprint
78
83
 
79
84
  # =========================================================================
80
85
  # Status Access
@@ -106,6 +111,21 @@ class NotebookState:
106
111
  """Get all reasons for a cell's staleness."""
107
112
  return self.get_status(cell_id).reasons
108
113
 
114
+ def set_fingerprint(self, cell_id: str, fingerprint: Optional[str]) -> None:
115
+ """Store the AST fingerprint of the source last executed for a cell.
116
+
117
+ Passing None (unparseable source) clears any stored fingerprint, which
118
+ makes subsequent edits fall through to the conservative mark-stale path.
119
+ """
120
+ if fingerprint is None:
121
+ self.fingerprints.pop(cell_id, None)
122
+ else:
123
+ self.fingerprints[cell_id] = fingerprint
124
+
125
+ def get_fingerprint(self, cell_id: str) -> Optional[str]:
126
+ """Return the AST fingerprint of the source last executed for a cell."""
127
+ return self.fingerprints.get(cell_id)
128
+
109
129
  def clear_pre_execution_reasons(self, cell_id: str) -> None:
110
130
  """Clear pre-execution reasons (NEVER_EXECUTED, CODE_CHANGED) from a cell.
111
131
 
@@ -487,6 +507,7 @@ class NotebookState:
487
507
  self.execution_seq.pop(deleted_cell, None)
488
508
  self.structural_reads_values.pop(deleted_cell, None)
489
509
  self.typed_changes.pop(deleted_cell, None)
510
+ self.fingerprints.pop(deleted_cell, None)
490
511
  if deleted_cell in self.cell_order:
491
512
  self.cell_order.remove(deleted_cell)
492
513
 
@@ -594,6 +615,7 @@ class NotebookState:
594
615
  self.execution_seq.clear()
595
616
  self.structural_reads_values.clear()
596
617
  self.typed_changes.clear()
618
+ self.fingerprints.clear()
597
619
 
598
620
  # =========================================================================
599
621
  # Debug/Inspection
@@ -113,11 +113,17 @@ def build_notebook_structure_message(cell_order: List[str]) -> dict:
113
113
  }
114
114
 
115
115
 
116
- def build_cell_edited_message(cell_id: str) -> dict:
117
- """Build a cell_edited message."""
116
+ def build_cell_edited_message(cell_id: str, source: Optional[str] = None) -> dict:
117
+ """Build a cell_edited message.
118
+
119
+ The optional source is the cell's current text; the kernel uses it to decide
120
+ whether the edit is meaningful (AST changed) or cosmetic (whitespace/comments).
121
+ Omitting it preserves the legacy always-mark-stale behavior.
122
+ """
118
123
  return {
119
124
  "type": CELL_EDITED,
120
125
  "cell_id": cell_id,
126
+ "source": source,
121
127
  }
122
128
 
123
129
 
@@ -2054,6 +2054,31 @@ class ReproducibilityEnforcer:
2054
2054
 
2055
2055
  return self.get_stale_cells()
2056
2056
 
2057
+ def set_fingerprint(self, cell_id: str, fingerprint: Optional[str]) -> None:
2058
+ """Store the AST fingerprint of the source last executed for a cell.
2059
+
2060
+ Used by [Inst-Edit] to distinguish meaningful edits from cosmetic ones
2061
+ (whitespace/comments). Computed by the kernel, which owns source access.
2062
+ """
2063
+ self._notebook_state.set_fingerprint(cell_id, fingerprint)
2064
+
2065
+ def get_fingerprint(self, cell_id: str) -> Optional[str]:
2066
+ """Return the AST fingerprint of the source last executed for a cell."""
2067
+ return self._notebook_state.get_fingerprint(cell_id)
2068
+
2069
+ def clear_code_changed(self, cell_id: str) -> List[str]:
2070
+ """Clear the CODE_CHANGED reason for a cell (symmetric [Inst-Edit]).
2071
+
2072
+ Called when an edit brings a cell's source back to an AST identical to
2073
+ what it last executed. Removes the CODE_CHANGED/NEVER_EXECUTED pre-execution
2074
+ reasons, marking the cell clean only if no other staleness reason remains
2075
+ (e.g. FORWARD_STALE from an upstream cell is preserved).
2076
+
2077
+ Returns current stale cells list.
2078
+ """
2079
+ self._notebook_state.clear_pre_execution_reasons(cell_id)
2080
+ return self.get_stale_cells()
2081
+
2057
2082
  def get_execution_records_size(self) -> int:
2058
2083
  """
2059
2084
  Calculate approximate memory size of execution records in bytes.
@@ -0,0 +1,243 @@
1
+ """
2
+ Tests for the meaningful-edit refinement of [Inst-Edit].
3
+
4
+ A source edit only marks a cell stale when it changes the cell's meaning. Edits
5
+ that touch only whitespace, blank lines, indentation, or comments are cosmetic
6
+ and leave the cell's status untouched; an edit that brings the source back to an
7
+ AST identical to the last execution clears the CODE_CHANGED reason.
8
+
9
+ Covers:
10
+ - FlowbookKernel._source_fingerprint (AST canonicalization, magics, syntax errors)
11
+ - NotebookState fingerprint storage (set/get/clear/delete)
12
+ - ReproducibilityEnforcer.clear_code_changed (symmetric clear)
13
+ - FlowbookKernel._process_cell_edit (end-to-end edit classification)
14
+ """
15
+
16
+ import pytest
17
+
18
+ from flowbook.kernel.flowbook_kernel import FlowbookKernel
19
+ from flowbook.kernel.notebook_state import NotebookState
20
+ from flowbook.kernel.models import Reason, ReasonType
21
+ from flowbook.kernel.tests.conftest import ReproducibilityTestHelper
22
+
23
+
24
+ def _make_kernel(enforcer=None):
25
+ """Build a FlowbookKernel shell with just enough wiring to test edits.
26
+
27
+ Uses __new__ to skip the heavy IPython kernel init, then attaches a real
28
+ IPython InteractiveShell (its input_transformer_manager makes magics
29
+ fingerprint correctly) and captures outgoing flowbook messages.
30
+ """
31
+ from IPython.core.interactiveshell import InteractiveShell
32
+
33
+ kernel = FlowbookKernel.__new__(FlowbookKernel)
34
+ kernel.shell = InteractiveShell.instance()
35
+
36
+ sent = []
37
+ kernel._send_flowbook_message = lambda msg: sent.append(msg)
38
+ kernel._sent_messages = sent
39
+ if enforcer is not None:
40
+ kernel._enforcer = enforcer
41
+ return kernel
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # _source_fingerprint
46
+ # ---------------------------------------------------------------------------
47
+
48
+
49
+ class TestSourceFingerprint:
50
+ def setup_method(self):
51
+ self.kernel = _make_kernel()
52
+
53
+ def fp(self, src):
54
+ return self.kernel._source_fingerprint(src)
55
+
56
+ def test_comment_only_edit_is_equal(self):
57
+ assert self.fp("x = 1 # original") == self.fp("x = 1 # changed comment")
58
+
59
+ def test_added_comment_line_is_equal(self):
60
+ assert self.fp("x = 1") == self.fp("# leading note\nx = 1")
61
+
62
+ def test_whitespace_and_blank_lines_equal(self):
63
+ assert self.fp("x=1") == self.fp("x = 1 ")
64
+ assert self.fp("x = 1") == self.fp("\n\nx = 1\n\n")
65
+
66
+ def test_reindented_block_is_equal(self):
67
+ a = "def f():\n return 1"
68
+ b = "def f():\n return 1" # different indent width, same structure
69
+ assert self.fp(a) == self.fp(b)
70
+
71
+ def test_string_literal_change_differs(self):
72
+ assert self.fp('x = "hello"') != self.fp('x = "world"')
73
+
74
+ def test_real_code_change_differs(self):
75
+ assert self.fp("x = 1") != self.fp("x = 2")
76
+ assert self.fp("x = 1") != self.fp("y = 1")
77
+
78
+ def test_underscore_numeric_literal_is_equal(self):
79
+ assert self.fp("x = 1000") == self.fp("x = 1_000")
80
+
81
+ def test_int_vs_float_literal_differs(self):
82
+ assert self.fp("x = 1") != self.fp("x = 1.0")
83
+
84
+ def test_magics_are_parseable_and_stable(self):
85
+ a = "%timeout 5\nx = 1 # a"
86
+ b = "%timeout 5\nx = 1 # b"
87
+ assert self.fp(a) is not None
88
+ assert self.fp(a) == self.fp(b)
89
+
90
+ def test_shell_command_parseable(self):
91
+ assert self.fp("!ls -la") is not None
92
+
93
+ def test_syntax_error_returns_none(self):
94
+ assert self.fp("x = ") is None
95
+ assert self.fp("def f(:\n pass") is None
96
+
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # NotebookState fingerprint storage
100
+ # ---------------------------------------------------------------------------
101
+
102
+
103
+ class TestNotebookStateFingerprints:
104
+ def test_set_and_get(self):
105
+ ns = NotebookState()
106
+ ns.set_fingerprint("a", "FP")
107
+ assert ns.get_fingerprint("a") == "FP"
108
+
109
+ def test_get_unknown_is_none(self):
110
+ ns = NotebookState()
111
+ assert ns.get_fingerprint("missing") is None
112
+
113
+ def test_set_none_clears(self):
114
+ ns = NotebookState()
115
+ ns.set_fingerprint("a", "FP")
116
+ ns.set_fingerprint("a", None)
117
+ assert ns.get_fingerprint("a") is None
118
+
119
+ def test_delete_pops_fingerprint(self):
120
+ ns = NotebookState()
121
+ ns.cell_order = ["a"]
122
+ ns.set_fingerprint("a", "FP")
123
+ ns.handle_delete("a")
124
+ assert ns.get_fingerprint("a") is None
125
+
126
+ def test_clear_drops_fingerprints(self):
127
+ ns = NotebookState()
128
+ ns.set_fingerprint("a", "FP")
129
+ ns.clear()
130
+ assert ns.get_fingerprint("a") is None
131
+
132
+
133
+ # ---------------------------------------------------------------------------
134
+ # Enforcer.clear_code_changed
135
+ # ---------------------------------------------------------------------------
136
+
137
+
138
+ class TestClearCodeChanged:
139
+ def test_clear_only_code_changed_goes_clean(self):
140
+ helper = ReproducibilityTestHelper()
141
+ helper.set_cell_order(["a"])
142
+ helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
143
+ sdc = helper.sdc
144
+ sdc._notebook_state.set_stale("a", {Reason(ReasonType.CODE_CHANGED)})
145
+ assert "a" in sdc.get_stale_cells()
146
+
147
+ stale = sdc.clear_code_changed("a")
148
+ assert "a" not in stale
149
+ assert sdc._notebook_state.is_clean("a")
150
+
151
+ def test_clear_preserves_other_reason(self):
152
+ helper = ReproducibilityTestHelper()
153
+ helper.set_cell_order(["a"])
154
+ helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
155
+ sdc = helper.sdc
156
+ sdc._notebook_state.set_stale(
157
+ "a",
158
+ {
159
+ Reason(ReasonType.CODE_CHANGED),
160
+ Reason(ReasonType.FORWARD_STALE, loc="x", cell_id="z"),
161
+ },
162
+ )
163
+
164
+ stale = sdc.clear_code_changed("a")
165
+ assert "a" in stale # FORWARD_STALE keeps it stale
166
+ reasons = {r.type for r in sdc._notebook_state.get_reasons("a")}
167
+ assert ReasonType.CODE_CHANGED not in reasons
168
+ assert ReasonType.FORWARD_STALE in reasons
169
+
170
+
171
+ # ---------------------------------------------------------------------------
172
+ # _process_cell_edit end-to-end
173
+ # ---------------------------------------------------------------------------
174
+
175
+
176
+ def _setup_executed_cell(source="x = 1"):
177
+ """Return (kernel, enforcer) with cell 'a' executed and fingerprinted."""
178
+ helper = ReproducibilityTestHelper()
179
+ helper.set_cell_order(["a"])
180
+ helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
181
+ sdc = helper.sdc
182
+ kernel = _make_kernel(enforcer=sdc)
183
+ sdc.set_fingerprint("a", kernel._source_fingerprint(source))
184
+ return kernel, sdc
185
+
186
+
187
+ class TestProcessCellEdit:
188
+ def test_cosmetic_edit_stays_clean_no_message(self):
189
+ kernel, sdc = _setup_executed_cell("x = 1")
190
+ kernel._process_cell_edit("a", "x = 1 # just a comment")
191
+ assert sdc._notebook_state.is_clean("a")
192
+ assert kernel._sent_messages == []
193
+
194
+ def test_whitespace_edit_stays_clean(self):
195
+ kernel, sdc = _setup_executed_cell("x = 1")
196
+ kernel._process_cell_edit("a", "\n\nx=1\n")
197
+ assert sdc._notebook_state.is_clean("a")
198
+ assert kernel._sent_messages == []
199
+
200
+ def test_meaningful_edit_marks_stale_and_emits(self):
201
+ kernel, sdc = _setup_executed_cell("x = 1")
202
+ kernel._process_cell_edit("a", "x = 2")
203
+ assert "a" in sdc.get_stale_cells()
204
+ assert any(m.get("type") == "metadata" for m in kernel._sent_messages)
205
+
206
+ def test_syntax_error_marks_stale(self):
207
+ kernel, sdc = _setup_executed_cell("x = 1")
208
+ kernel._process_cell_edit("a", "x = ")
209
+ assert "a" in sdc.get_stale_cells()
210
+
211
+ def test_revert_to_last_run_clears_staleness(self):
212
+ kernel, sdc = _setup_executed_cell("x = 1")
213
+ # First, a meaningful edit marks it stale.
214
+ kernel._process_cell_edit("a", "x = 2")
215
+ assert "a" in sdc.get_stale_cells()
216
+ kernel._sent_messages.clear()
217
+ # Editing back to source with the last-run AST clears CODE_CHANGED.
218
+ kernel._process_cell_edit("a", "x = 1 # back to original")
219
+ assert sdc._notebook_state.is_clean("a")
220
+ assert any(m.get("type") == "metadata" for m in kernel._sent_messages)
221
+
222
+ def test_cosmetic_edit_preserves_other_staleness(self):
223
+ kernel, sdc = _setup_executed_cell("x = 1")
224
+ sdc._notebook_state.set_stale(
225
+ "a", {Reason(ReasonType.FORWARD_STALE, loc="x", cell_id="z")}
226
+ )
227
+ kernel._process_cell_edit("a", "x = 1 # comment only")
228
+ # Still stale for the upstream reason; no spurious status flash.
229
+ assert "a" in sdc.get_stale_cells()
230
+ reasons = {r.type for r in sdc._notebook_state.get_reasons("a")}
231
+ assert ReasonType.FORWARD_STALE in reasons
232
+ assert kernel._sent_messages == []
233
+
234
+ def test_unexecuted_cell_without_fingerprint_marks_stale(self):
235
+ # No record / no fingerprint -> falls through to conservative mark.
236
+ helper = ReproducibilityTestHelper()
237
+ helper.set_cell_order(["a"])
238
+ helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
239
+ sdc = helper.sdc
240
+ kernel = _make_kernel(enforcer=sdc)
241
+ # No set_fingerprint -> get_fingerprint is None -> mark stale on any edit.
242
+ kernel._process_cell_edit("a", "x = 1 # comment")
243
+ assert "a" in sdc.get_stale_cells()
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbook",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Reproducibility enforcement for Jupyter notebooks: a JupyterLab extension that enforces rerun consistency by tracking variable- and column-level dependencies between cells.",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -139,7 +139,7 @@
139
139
  "outputDir": "flowbook/labextension",
140
140
  "schemaDir": "schema",
141
141
  "_build": {
142
- "load": "static/remoteEntry.97e370ce33befeb5451b.js",
142
+ "load": "static/remoteEntry.9c89c62fdbe6db112e2d.js",
143
143
  "extension": "./extension",
144
144
  "style": "./style"
145
145
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbook",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Reproducibility enforcement for Jupyter notebooks: a JupyterLab extension that enforces rerun consistency by tracking variable- and column-level dependencies between cells.",
5
5
  "keywords": [
6
6
  "jupyter",