Updated: 2026-06-12
This is the single, consolidated record of ProbeFlow's code-review history. The
detailed per-angle review files (docs/reviews/2026-05-27-*.md) and the earlier
staged-review stage files were pruned once their conclusions were captured here,
following the project convention: keep a living summary, prune the detail once
findings are enacted. The conclusions and the remaining open items survive below.
Three review efforts have run on ProbeFlow:
- Staged review (2026-05, "Stage 1/2/3") — scientific-workflow/physics pass, an architecture/maintainability pass with bounded refactor slices, and a compatibility/stability/release-safety pass. All concrete findings were enacted.
- Deep review (2026-05-27) — six parallel angles (physics, numerical stability, image-processing pipeline, IO/instrument format, backend architecture, GUI architecture) producing 114 findings (S0=1, S1=36, S2=61, S3=16).
- Adversarial review campaign (2026-06-09 → 06-12, PRs #20–#36) — targeted seam-by-seam passes over the areas the earlier reviews could not exercise (GUI timing, async loading, replay fidelity, parsers under corruption). See the dedicated section below.
Status: essentially complete. Every S0 and S1 finding, and every physics / numerical-correctness finding, has been enacted. What remains (listed below) is lower-severity S2/S3 polish — mostly GUI-architecture tidiness, large-file splits, and a handful of numerical/IO edge cases. None block users.
Scientific correctness:
- Lattice correction —
_polar_decomposeno longer flips singular values on reflective input (the S0); reflective transforms are now flagged, not silently applied. Calibrated physical lattice vectors and per-axis residuals throughout. - FFT / window cluster — unified window vocabulary, unbiased masked-ROI DC subtraction, coherent-gain normalisation, periodic Hann semantics, isotropic cutoffs, plateau-safe Bragg-peak finding.
- Calibration threading —
apply_processing_statenow takes scan calibration; pixel sizes reachsubtract_background(step_tolerance)/facet_level, andscan_range_mis updated by shape-changing geometric ops so scale bars, FFT k-axes, and pixel→nm conversions stay correct. quantize_bit_depth— explicit reproduciblevmin/vmax/quantum, run after arithmetic ops, preserving the "discrete level" export guarantee.- Periodicity/autocorrelation bias, pair-correlation edge correction, row-align ordering, and assorted estimator fixes.
IO robustness:
- SXM writer overwrite/source-equality guard; ROI sidecar path no longer collapses
dotted Createc filenames; Createc channel-count trusts the header; Nanonis
missing tip position → NaN (not origin); DAT→SXM
SCAN_DIRorientation; SI unit normalisation; decode warnings surfaced onScan.warnings; structured CSV and provenance-sidecar coverage for all exporters.
Architecture / maintainability:
- Canonical
measurements.models.MeasurementResultwith a single legacy adapter; unifiedFeaturePoint; shared kernels undermeasurements/(formatting,roi_resolve,raster);ProcessingStep→ProvenanceStep(alias kept). ScanGraphandplugins/shelved as explicitly experimental (dropped from the publicprovenancenamespace); boundary-rules docstrings rewritten to describe the real architecture (Scan → ProcessingState → ProcessingHistory + ExportRecord).- GUI modeless-dialog lifecycle:
ImageViewerDialognow owns a_modeless_childrenregistry closed oncloseEvent; worker-signals race fixed; central viewer command/shortcut registry (gui/viewer/shortcuts.py).
Release safety: Qt headless test-harness skip; toolbar assets verified in the wheel; CLI surface verified; backend imports remain GUI-free.
Every item that was still listed as open was re-verified against the current code. The result: the overwhelming majority were already resolved by the refactoring that landed after the review docs were written, or were intentional design choices rather than defects. Details below.
Physics / numerical:
_polar_decomposeis the reworked PSD form (S always PSD, R inherits sign of det, NaN +UserWarningon reflection). NumPy SVD returns non-negative singular values, so the "tiny negative singular value" concern cannot arise.compute_correction'stol = 1e-6·|a||b|compared against|det|is asin(angle)test, sincedet = |a||b|·sinθ. Scale-invariant by design.line_profilealready usesmode="constant", cval=NaN(noreflectleakage) and a symmetric perpendicularlinspace(nowidth_px=1asymmetry)._robust_scalealready has a graded fallback (max-abs·1e-3, theneps), not a bareeps.subtract_backgroundstep-tolerance already falls back to the full fit mask when too few candidates pass (candidate.sum() >= n_terms)._subtract_scanline_backgroundpreserve_levelintentionally restores a single global reference level; per-row "preservation" would undo the subtraction.extract_latticecluster_choice=1selects the most populated cluster (a sensible, user-tunable default), not an arbitrary one.current_histogramhandles empty input via NumPy's defined empty-array behaviour (no crash, no bad rounding).
Image processing:
_nan_normalized_gaussianuses normalized convolution (mode="constant"+ weight division) — noreflectboundary leakage.threshold_image(mode="binarize")returns float64 deliberately so non-finite pixels stay NaN (bool/uint8 cannot) and stay pipeline-compatible.
Backend / GUI architecture:
_legacy.pyno longer exists (removed).FeatureCountingWindowwas extracted into thegui/features/package with a dedicatedcontroller.py(the ~150 LOC dedup) and now takes/propagates a theme viaapply_theme(no moret={}).gui.processingno longer uses a wildcard import (explicit imports now).- A central viewer command/shortcut registry exists (
gui/viewer/shortcuts.py).
feature_points_to_csvnow rejects non-finite / non-positivepixel_size_*_nminstead of silently emitting meaningless nm columns.- Large-file splits — done. The three monoliths were broken up along their
natural seams (behaviour preserved; methods/functions moved verbatim and resolve
via the class MRO / module re-exports, verified by ruff
F821, class assembly, and the runnable backend tests):fft_viewer.py3044 → 1769 LOC, withfft_viewer_lattice_mixin.py,fft_viewer_mains_mixin.py,fft_viewer_reconstruct_mixin.py.image_viewer.py2293 → 627 LOC, withimage_viewer_build_mixin.py(the_buildUI assembly) andimage_viewer_chrome_mixin.py(menu bar, sidebar/tool hosting, modal overlays, window layout).filters.py1214 → 577 LOC, with the Bragg/reciprocal-lattice analysis moved tobragg.pyand re-exported for backward compatibility.
A second deliberate re-verification (prompted by how many earlier "open" items turned out already-done) confirmed that essentially the entire deferred list had been fixed during the deep-review campaign without being logged against its finding number. Verified resolved / intentional in the current code:
- Layering:
apply_processing_stateno longer hides disk I/O — it takes anoperand_resolverinjection point with the file-load isolated in a documented default (cites arch-backend #13).Scan's lazyprocessing.*imports are the intended way to break the core←processing cycle. - IO (RHK SM4 + SXM):
_dtype_from_data_sizenow uses thedata_sizeheader with a warned payload-length fallback (#7);_parse_string_datawarns and stops on a truncated block instead of silently dropping (#12);_parse_page_index_arrayadvances byPAGE_INDEX_ARRAY_SIZE + object_count·OBJECT_SIZE, not a fixed stride (#13); the SXM cushion cache was cleanly refactored into_cushion_tail_lens_data_offset_in_filewith accurately-named byte caches (#22).
- Image processing:
affine_lattice_correctionnow uses weight-normalized NaN-aware OOB handling, matching the rotate path (#8); the threshold dialog's display range is percentile-based, not raw extrema (#13). - GUI:
ThresholdDialogowns its ownHistogramPaneland uses its public API (#6); the three thumbnail setters share_apply_thumbnail_setting(#14); saved processing is keyed by path and mtime (#10);closeEventwrites config via the centralized_save_viewer_desktop_layout_intohelper (#16); survey availability is probed before_build_ui(#17);desktop_layoutrestore falls back gracefully when splitter counts differ (#18);FeatureCountingWindownow hastest_feature_counting_controller.py+test_gui_features.py(#21).
What is left is purely structural preference: no defect, no functional benefit, and a real import-churn/regression cost to change. Deliberately not pursued.
- Spectroscopy logic spread across three packages;
processing/analysis.pyholds analysis-style measurements despite living inprocessing/; many-argument measurement constructors could take a context object. _collect_point_sources_mreads the measurement controller's (non-underscore)feature_points/feature_metadataviagetattrdefaults — minor coupling, acceptable as-is.
A third effort ran as short adversarial passes, each shipping its fixes with regression tests before moving on. One PR per pass; the PR descriptions hold the per-finding detail.
- GUI robustness (#20) — QPixmap built off the GUI thread, worker-signal
lifetime (
SIGSEGVclass), thread-pool drain at exit. - Browse loading at scale (#21) — network-drive freezes: async preview / index workers, metadata peek budget, sliced card building, thumbnail priority.
- Seam review, two passes (#22–#26) — scoped-filter replay ordering, browse async/navigation seams, sidecar discovery fallback, corrupt-sidecar visibility, quick-selection lifecycle, scale/shear overlay transforms, and the PySide wrapper-recycling test flake (root-caused and fixed).
- Physics reviews (#27, #29) — spectroscopy: the qPlus setpoint gate (Δf misread as amps), time-axis sanitisation, derivative units; FFT: window-envelope compensation and odd-size soft-border centring.
- User-feedback batch (#28, #30) — mains custom streak pairs, notch-width visualisation, background notch fill, streak/overlay decoupling.
- Workflow-replay harness (#31) — a permanent integration harness asserting display == export == provenance-replay for representative pipelines (plus a set-zero frame fix it caught).
- Parser adversarial review (#32–#34) — mutated-fixture corpus tests for every reader; strict VERT metadata summaries (fast path now agrees with the full parse), partial-load warnings surfaced in the viewer, Nanonis single-column fix, and recognition of the normal Createc trailing appendix (no more spurious warnings on healthy files).
- Docs (#35–#36) — GUI guide with offscreen-generated screenshots.
Suite grew from ~2,260 to 2,449 tests across the campaign; main is green.
- A true Python 3.11/3.12 test matrix (only the local interpreter was available).
- GUI smoke tests on a working Qt platform (local Qt cannot init
QApplication; GUI tests skip under the preflight guard).
docs/gui.mddocs/cli.mddocs/createc_dat_reader.mddocs/roi_manual_test_checklist.mddocs/notes/roi-display-notes.mddocs/core_derisk_plan.md(completed plan, kept as a design record)