Repository navigation
PP-8c: fixes from running on real data (file modes, absent-axis sizes, masks inputs, stopped-run text, read arguments, design command) - #117
Merged
Conversation
Atomic writes went through tempfile.mkstemp, so every output was 0600 whatever the umask and colleagues could not read results in a shared folder. A new file now gets 0666 & ~umask; a file that existed keeps its own mode. The table (written by the normalization brick's writer into the staging folder) is given its mode when it is published. POSIX only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Plan question 50 of PP-8c renumbered 52, after PP-8b's 50 and 51. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
A CYX max projection kept its stack's z-step (HPMEC: Z 0.43 um) and the run recorded it as the image's Z size. A Z or T size is now recorded only for an image whose roles include that axis (calibration.fit_to_roles, applied after the axis review in run and check). A calibration.csv statement for an absent axis is the warning calibration_axis_absent naming the images, never a recorded size. The report text counts Z and T sizes for the images with the axis. RowKey has no pixel sizes, so earlier rows are reused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…umber A plane_id passed as z gave the misleading IndexError "has no Z ..." and read_mask(z=True) reached the reader. Both now raise a TypeError naming the expected arguments; Python and NumPy integers are accepted, read_mask keeps None. No read by plane_id is added (spec: read_plane(fov, z, t)). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Every run holding the lock writes its report, and the CLI run writes the design template when it stops on the design, yet the summary said "Nothing was written". It now says no table or manifest was written and names the report and the template when written (RunResult.report_written, run_summary(design_template=)); the notebook says the same. A run refused by the lock still says "Nothing was written." Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…change) _masks becomes masks_command, _THRESHOLD_HELP THRESHOLD_HELP, and the command's own options are added by add_masks_arguments; cli.py keeps the parser and the shared options. Layer: ui. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
The experiment argument is optional when the output folder's v2 manifest records it (experiment_not_recorded otherwise); the masks folder, mask axes and exclusions are the recorded ones unless given (--no-exclude clears the exclusions). A value given that differs is used for the scan and named; the manifest is not changed. The recorded-experiment lookup is one helper shared with run (cli_run.use_recorded_experiment). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
check also writes experiment_design.csv prefilled from the folders when the output folder holds none (under the lock, folders without errors; never over a design). design --output OUT prints the design and what is still unconfirmed, and changes it without editing the file (--repeat-group, --control, --no-control, --confirm, --confirm-all) under the lock with the run's reader and validator; unknown names and groups breaking the rules are refused with nothing changed. Python: edit_design with DesignEdit (apply_design_edit without files). Nothing is confirmed by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
The action now offers the design command (--confirm) next to step 1 of the notebook and editing experiment_design.csv. design.py keeps its line count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
guijacquemet
marked this pull request as ready for review
November 2, 2026 08:32
The output_locked message tells users to give --remove-stale-lock to the command, which design and calibrate did not take. Both now do, with the same rule as check, run and masks: removed only on that request, never the lock of a process still running on the same computer. Plan Q57, contract and user guide updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Contributor
Author
Independent review: merged (merge commit a4e30a7)Finding fixed in review
Verified
Gate (own worktree and venv,
§9 figures (touched code)
Branch: it was auto-deleted on merge and restored at abcf59d. Follow-up (not blocking): the notebook's folder check runs before the roles are confirmed, so it cannot fit Z/T sizes to roles. The notebook's run does. Generated by Claude Code |
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the owner decisions of 2026-11-02 found by running main on real data (fiberfeatures
data/experiment_001, MorphoRelatedata/hpmec_perfusion). Based on main, brought up to date with PP-8b (#115) and #116 by a merge (no rebase; one conflict indocs/plan.md, resolved by keeping PP-8b's questions 50–51 and numbering this PR's decisions 52–57). Version stays 0.3.4.Summary (by module)
_io.py:new_file_mode()(0666 & ~umask; umask read from/proc/self/status, else by a round trip under a lock with the most restrictive value while set) andgive_output_mode(temporary, target): a file that existed keeps its mode, a new file gets theopen()mode.write_text_atomicuses it, so the report, design,calibration.csv,mask_relationships.jsonand the staged manifest get it.run/publish.py: gives each staged file its mode beforeos.replace, which coversplane_histograms.csv(written 0600 by the normalization brick's writer; that repo is not changed).calibration.fit_to_roles: Z/T sizes are kept only for images whose roles (not singleton roles) include them. Acalibration.csvstatement for an absent axis gives the new warningcalibration_axis_absentnaming the images, and is not recorded for them.run/preflight.fit_calibrationis applied after the axis review inrun(pipeline._locked_run, beforecalibration_change) and incheck.discovery_report: the text addsZ step:/Time step:counts for images with that axis, and the number without it.RowKeyholds no pixel sizes, so earlier rows are reused: the nextrestartleaves the table byte-identical (tested) and rewrites the manifest without the absent sizes.masksuses recorded inputs (plan Q56, contract §9.2)cli_masks.py.experiment_not_recordedwithout a v2 manifest).--no-excludeadded for parity withrun). A differing value is used for the scan and printed asChanged …("used for this scan only; the run keeps the recorded value until it is given this one"). The manifest is untouched.cli_run.use_recorded_experimentis shared byrunandmasks.RunResult.report_writtenis set andreport.run_summary(result, design_template=)is used.read_plane/read_maskargument types (plan Q54, contract §9.1)run_inputs._coordinate: a non-integerz/t(aplane_id, str, float, bool) raisesTypeErrornamingread_plane(fov, z, t)/read_mask(fov, mask_type, z, t)and pointing atPlaneRecord.z/.t.read_maskkeeps None.plane_idis added (spec §6 interface; every row carries its z and t).check;designcommand (plan Q57, contract §12, §9.2)run/check_step.write_check_outputs(CheckOutputs):checkwrites the report and, under the same lock hold,experiment_design.csvprefilled from the folders, only when none exists and the folders have no error. It never replaces a design.write_check_reportis unchanged.design_edit.py:DesignEdit,apply_design_edit,parse_repeat_group,DesignEditError. Moves, then controls, then confirmations. A group to confirm is validated bydesign_issueson its rows. Unknown folder/condition/group, contradictions, conflicts and invalid names are refused with nothing changed.run/design_file.py:edit_designreads (read_experiment_design), changes and writes under the lock;write_design_template.cli_design.py:design --output OUT [--repeat-group C/R=G]… [--control C]… [--no-control C]… [--confirm G]… [--confirm-all] [--remove-stale-lock]prints the design table (report.design_table), what is unconfirmed and any conflict. Nothing is confirmed by default.DesignEdit,DesignEditError,apply_design_edit,edit_design.design_not_confirmedaction now names thedesigncommand.--remove-stale-lockfordesignandcalibrate(owner decision 2026-11-02, plan Q57 note, contract lock row, user guide). Theoutput_lockedmessage tells users to give it to the command. Same rule ascheck/run/masks: removed only on request, never the lock of a process still running on the same computer. Tests:tests/test_stale_lock_design_calibrate.py(5, all fail before the fix).Decisions (each recorded in plan §12)
chmod a+ror deletes them; the user guide says so.--no-exclude), beyond the three inputs listed. Otherwisemaskswould read masks of images the run left out.--confirm-allis a separate flag rather than--confirm all, so a group namedallstays confirmable.designworks on the rowscheck/runwrote and does not rediscover folders; the run's reconcile reopens a group that gained a folder, as before. Q57 also records thatcheckwriting the template amends spec §1 ("writes only the report"), and (review) thatdesign/calibratetake--remove-stale-lock.Notes for review
design.py(964 lines, baselined) andnotebook_ui.py(3051, baselined) are changed in place with the same line counts; the new design code is in new modules."uncalibrated", which hides its Z/T entries (unchanged behaviour). The new text counts treat such an image's Z as unknown.InputChangeis reported when a restart drops an earlier version's absent-axis size from the manifest: it is a code change, not an input change.run_experiment).Tests
ruff checkandruff format --checkclean;tools/check.py --rulesclean..[dev,notebook,zarr]: 1575 passed, 1 skipped after review (1570 before abcf59d; main d35db1e: 1527 passed, 1 skipped). The skip istest_discovery.py:665"running with permissions that ignore file modes" (root, expected).src(d35db1e): 50 failed, 3 passed (45 + the 5 review tests). The 3 that pass are positive controls: an image with Z/T keeps its header sizes; NumPy integers and None are still accepted.test_output_permissions.pytest_absent_axis_sizes.pytest_read_arguments.pytest_stopped_run_text.pytest_masks_recorded_inputs.pytest_design_command.pytest_stale_lock_design_calibrate.py(review)test_cli,test_axis_check,test_report,test_notebook_ui), and 2 tests thatcheckwrites only the report (now the report and the template;run_files.check_outputs).§9 figures (measured on every function this PR touches)
run_inputs.py559,report.py537,calibration.py460,discovery_report.py457,cli_run.py452,pipeline.py452. New files:design_edit.py172,cli_masks.py148,cli_design.py145,run/design_file.py99;cli.py298.design.py(964) andnotebook_ui.py(3051) are baselined, at the same counts as on main.pipeline._publish, unchanged apart from one argumentmasks_command, movedcli._check,cli_masks.masks_commandcli._checkcli_masksui,cli_designui,design_editpure,run.design_fileio.noqatotal 88 on main and head. No new exemptions.Real-data check (branch code; outputs in a scratch folder, data read-only; umask 022)
fiberfeatures
experiment_001(17 YX uint16, 2 conditions,reference/folder):checkexit 0:reference/and the emptyreferencefolder.design --outputprints the table (condition_1 single single no 9 0 no/condition_2 … 8 … no) and "Not confirmed yet: single".design --confirm nopeexits 1: "The experiment design was not changed: unknown repeat group 'nope': the design's repeat groups are single".design --confirm-all --control condition_1prints "…, updated" and "yes, 2026-11-02" in both rows, then "Every repeat group is confirmed."run --axes YXexit 0: "Wrote 17 planes…", 2 warnings, 3 intensity warnings.run --outputalone: "Unchanged (rows kept): 17 images".-rw-r--r--.MorphoRelate
hpmec_perfusion(2 CYX max projections, masks cells/mito/nuclei):check --maskswrites the template and the report.run --axes CYXbefore confirming, exit 1:run --output/masks --outputwith no run recorded yet:experiment_not_recorded(correct).design --confirm single --control HPMEC_ctrlconfirms both rows.run --masks … --axes CYXexit 0: "Wrote 2 planes", "Checks: no problems found".('Z', None, None), ('T', None, None)(main recorded Z 0.43 µm from the header). Report: "Pixel sizes: 2 from the header…", with no Z step line.masks --outputalone exit 0: 3 mask types, "nuclei inside cells: 146 of 146", "mito grouped by the labels of cells", hierarchy cells → mito, nuclei.RunInputs.read_plane(fov, fov.plane_ids[0])raisesTypeError: read_plane(fov, z, t) takes the plane's zero-based coordinates z and t as whole numbers; z is 'HPMEC_cond2/single/Position_2.tif::z=0,t=0' (str)….-rw-r--r--. On main, the same commands left-rw-------files, recorded Z 0.43 from the header, and printed "Nothing was written (1 error)" after writing the report and the template.🤖 Generated with Claude Code
https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc