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.
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/FORMAL_DEVELOPMENT.md +26 -7
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/PKG-INFO +5 -2
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/README.md +4 -1
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/_version.py +1 -1
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/flowbook_kernel.py +102 -18
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/notebook_state.py +22 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/protocol.py +8 -2
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/reproducibility_enforcer.py +25 -0
- flowbook_python-0.1.2/flowbook/kernel/tests/test_meaningful_edit.py +243 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/package.json +2 -2
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/schemas/flowbook/package.json.orig +1 -1
- flowbook_python-0.1.0/flowbook/labextension/static/873.3ca7ae352f965bccc339.js → flowbook_python-0.1.2/flowbook/labextension/static/873.3edec525c5c79ec55cd6.js +1 -1
- flowbook_python-0.1.0/flowbook/labextension/static/remoteEntry.97e370ce33befeb5451b.js → flowbook_python-0.1.2/flowbook/labextension/static/remoteEntry.9c89c62fdbe6db112e2d.js +1 -1
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/session.py +35 -11
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/package.json +1 -1
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/executionhook.ts +8 -5
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/protocol.ts +3 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/agents/reproducibility-fixer.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/basic-run-nbi.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/basic-run.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/categorize-repro-errors.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/fix-notebook.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.claude/commands/sync-spec.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.copier-answers.yml +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.gitignore +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.prettierignore +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/.yarnrc.yml +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/CLAUDE.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/CLI.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/CONFLICT_RELATION.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/LICENSE +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/MCP_ARCHITECTURE.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/FlowBookTutorial.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/GettingStarted.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/cudf/CudfCheckpointBenchmark.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/cudf/CudfGpuCheckpoint.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/cudf/CudfGpuPipeline.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/01_Basic_Tracking.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/02_Wrong_Order.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/03_Rejected_Overwrite.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/04_Column_Independence.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/05_Column_Conflict.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/06_Edit_Detection.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/09_UnrecoverableMutation_Lists.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/10_UnrecoverableMutation_Arrays.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/demos/11_UnrecoverableMutation_DataFrameColumns.ipynb +0 -0
- {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
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/00_NoReadAndWrite.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/01_WriteBeforeRead.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/02_NoReadBeforeWrite.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/03_NoWriteAfterRead.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/04_ForwardStale.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/examples/litmus/05_BackwardStale.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/__main__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/__main__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/baseline_kernel.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/baseline_kernel/kernelspec/kernel.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/__main__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/checkpoint_client.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/checkpoint_kernel.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/checkpoint_kernel/kernelspec/kernel.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/cli.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/compare_fixed_overhead.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/compare_overhead.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/flowbook_timers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/helpers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/metadata_cli.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/models.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/optimization_metadata.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/plot_extraction.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/plot_rendering.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/show_errors.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/slurm_logs.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/stats_cli.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/stats_display.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_flowbook_timers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_metadata_format.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_models.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_models_v5.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_plot_extraction.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_plot_extraction_v5.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_plot_rendering.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/cli/tests/test_rerun_overhead_plots.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/handlers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/__main__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/access_events.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/change_detector.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/changes.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/flowbook_client.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/kernelspec/kernel.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/kernelspec/logo-32x32.png +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/kernelspec/logo-64x64.png +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/loc_ids.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/locations.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/models.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/LITMUS_TESTS.yaml +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/conftest.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/generate_litmus_latex.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/litmus_helpers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/litmus_output/litmus_tests.tex +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/litmus_output/litmus_tests.txt +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_alias_conflicts.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_change_detector_locs.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_column_staleness_bug.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_converters.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_delete_transitions.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_flowbook_kernel.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_forward_dependency.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_implementation_theory_diff.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_inplace_mutation_detection.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_litmus.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_loc_ids.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_locations.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_locset_integration.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_measure_rerun_overhead.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_notebook_state.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_optimizations.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_order_changes.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_order_changes_complex.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_paper_examples.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_protocol.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_provenance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_removed_writes_scenario.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_reproducibility_enforcer.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_reproducibility_structural.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_typed_predicates.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_uncopyable_as_write.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel/tests/test_var_binding_semantics.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_discovery.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/ast_utils.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/base_client.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/base_kernel.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/checkpoint.py.backup +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/column_provenance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/column_tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/cudf_compat.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/deepcopy.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/deepcopyable.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/df_subset_detector.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/diff.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/display_helpers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/experimental_client.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/extended_types.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/file_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/fs_magics.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/heap_size.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/install.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/json_utils.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/kernelspec/kernel.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/kernelspec/logo-64x64.png +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/locals.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/memory_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/models.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/opaque.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/process_cleanup.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/structural_tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/test_diff.py.backup +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/test_subset_perf.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_ast_utils.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_base_kernel.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_catboost_pool.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_chained_assignment_error.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_comprehensive.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_corner_cases.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_df_subsets.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_functions.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_memory_sharing.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_nested.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_object_conversion.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_checkpoint_overhead_measurement.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_provenance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking_methods.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking_performance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_column_tracking_setitem_reads.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_checkpoint_dedup.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_checkpoint_inflation.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_checkpoint_perf.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_compat.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_proxy_fingerprint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_cudf_proxy_tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_deep_alias_detection.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_deepcopy_preserve_mode.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_deepcopyable.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_df_subset_detector.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_byte_comparison.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_deep.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_float_tolerance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_keras_catboost.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_object_float.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_diff_structural.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_display_helpers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_extended_types_coverage.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_extension_dtypes.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_file_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_file_checkpoint_comprehensive.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_fs_magics.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_gpu_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_gpu_checkpoint_perf.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_heap_size.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_incremental_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_index_deepcopy.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_install.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_json_utils.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_keras_deepcopy.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_large_list_cache.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_lightgbm_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_locals.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_checkpoint_comprehensive.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_checkpoint_edge_cases.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_memory_snapshot.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_models.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_models_coverage.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_multiindex.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_keras.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_keras_gaps.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_ml.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_pytorch.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_opaque_pytorch_gaps.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_pytorch_diff.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_shap_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_stacking_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_structural_tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_targetencoder_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_timeout_handler.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking_coverage.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking_performance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_tracking_structural.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_types_coverage.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tests/test_virtual_fs.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/timeout_handler.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/tracking.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/types.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/kernel_support/virtual_fs.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/schemas/flowbook/plugin.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/728.b1df4bca1a3305d0d0a7.js +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/905.94c2bfb401597cc2a103.js +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/905.94c2bfb401597cc2a103.js.LICENSE.txt +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/951.ba84389925d6a0676e79.js +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/style.js +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/labextension/static/third-party-licenses.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/jupyter_config.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/server.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_cell_validation.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_contents_api.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_jupyter_config.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_kernel_discovery.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_new_tools.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/mcp/tests/test_session_integration.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/MANUAL_UI_TESTS.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/claude_commands/flowbook-nb-fix.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/extension.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/extension_data/extension.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/session.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/test_extension.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/test_session.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tests/test_tools.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/nbi/tools.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/fix_repro_errors.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/parse_repro_errors.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/scripts/tests/test_fix_repro_errors.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/base.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/comm_models.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/compare_baseline.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/execute.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/execute_base.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_compare_baseline_rerun.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_compare_baseline_v3.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_downsample_csv.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_flowbook_update_protocol.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_gpu_memory_recording.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/commands/tests/test_path_traversal.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/handlers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/kernel_helper.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/kernel_manager.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/registry.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/tests/test_handlers.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/server/tests/test_kernel_manager.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/slurm/slurm_cli.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/README.md +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/benchmark_checkpoint.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/checkpoint_overhead_test.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/correctness.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/kernel_comparison.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebook_loader.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/data_pipeline.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/dependencies.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/deterministic.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/ml_workflow.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/multi_dataframe.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/nondeterministic.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/notebooks/pandas_heavy.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/performance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/plot_checkpoint_percentage.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/plot_checkpoint_timings.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/results.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/runner.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/run_correctness.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/run_kernel_comparison.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/testing/scripts/run_performance.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/cell_ids.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/cell_index.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/dependencies.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/flowbook_metadata.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/gpu_memory.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/kernel_installer.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/liveness.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/metadata_extractor.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/model_copy.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/nb_diff.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/notebook_analysis.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/notebook_to_python.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/output.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/prompts.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/prompts.yaml +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/__init__.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_cell_ids.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_cell_index.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_dependencies.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_gpu_memory.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_liveness.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_notebook_analysis.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/tests/test_notebook_to_python.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/flowbook/util/text.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/install.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/jupyter-config/server-config/flowbook.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/media/flowbook-small.png +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/media/flowbook.png +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/pyproject.toml +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/schema/plugin.json +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/run-base.sh +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/run-flowbook.sh +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/timers.sh +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/scripts/type_coverage.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/setup.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/api.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/executiondialog.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/cellhighlighter.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/celltoolbar.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/executionhook.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/history.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/historypanel.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/manager.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/metadatapanel.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/plugin.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/toolbar.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/types.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/unittestpanel.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/experimental/unittesttracker.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/kernel.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/logpanel.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/_archived/messagecomponents.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/cellindex.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/cellindexutils.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/cellhighlighter.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/dependenciespanel.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/metadatapanel.tsx +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/nbibridge.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/plugin.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/stalenessmanager.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/stalenessnotice.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/toolbar.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/types.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/flowbook/violationnotice.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/handler.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/index.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/shared/kerneldetection.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/src/shared/types.ts +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/_archived.css +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/base.css +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/dependencies.css +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/index.css +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/style/index.js +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/demo_diffresult_basemodel.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/demo_markdown.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/demo_test_comm_structure.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_comm_simple.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_diff_refactor.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_diffresult_serialization.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_dtype_compatibility.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_env_in_optimize.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_execution_error_capture.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_generate_with_env.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_markdown_format.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_notebook.ipynb +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_command.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_fixes.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_keys_to_include.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_optimize_validation.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_partial_execution_bug.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_progress_messages.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_prompt_integration.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_real_kernel_message_capture.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_test_comm.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tests/test_validate_change_command.py +0 -0
- {flowbook_python-0.1.0 → flowbook_python-0.1.2}/tsconfig.json +0 -0
- {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.
|
|
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
|
|
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
|
|
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`:
|
|
@@ -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
|
|
583
|
-
"""
|
|
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
|
-
|
|
587
|
-
if
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|