Repository navigation
Pre-release follow-ups to #254 and #255 - #257
Conversation
- A header change (channels_changed) reopens the replicate's "same way?" (experiment-v1 section 10: "a unit's ordered channel list"): published_answers.reopened takes the reconciliation. - describe (CLI) reads the image headers and compares them with observe_folders(..., metadata=), as the notebook does. - Save stays allowed when the run's checks refuse (owner decision, 2026-11-10); a line beside the button says the run will stop and why. - The step-by-step preview tick is gated on _preview_drawn of the current roles; a failed preview takes an earlier tick back. - channel_strip, the native crop and the Images found picture check the bound fingerprint before and after the read. - A unit's channel count comes from every image (counts_agree, unit_channel_counts): disagreement leaves it unknown. - "The run can start" only once the run's checks have run and passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
After a header change, R_HEADER_CONTRADICTION appeared only once the reopened replicate was answered again: a refusal hidden until the user acts. While answers are missing, Check and save now says the run is blocked by both: the unanswered question and the run's refusal of the saved description (codes and folders), or, before the axis roles are confirmed, the header change the folder check found. Also drop planes read across a rewrite from the preview cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
guijacquemet
left a comment
There was a problem hiding this comment.
Adversarial review: changes requested, not merged.
Pushed f8b57e3 (coordinator decision): a refusal is no longer hidden behind the reopened "same way?". While answers are missing, Check and save shows "The run (step 12) is blocked by both:" with (1) the reopened question and (2) either the run's refusal of the saved description (codes, messages and folders), or, before the axis roles are confirmed, the header change the folder check found. Tests: the extended test_a_header_rewritten_under_the_same_files_keeps_its_row_open and the new test_a_header_change_before_the_axes_are_confirmed_is_shown_as_blocking. Both fail on a75915f. Also fixed: channel_strip cached the planes of a refused read under the old fingerprint, so a file restored with the same size and mtime would have been served from those torn planes. It now discards them, with a regression test.
Blocker (item 1 against §4.4a): suppose the header change is fixed in the description and not by touching the files. For example, a rename DAPI→Hoechst is fixed by adding Hoechst to DNA.header_names. Then the unit's channels list and repeat_layout stay the same. But published_answers.reopened has already dropped rep1's confirmed_on, because the reconciliation against the published file still says channels_changed. The user's new answer is dated today. check_candidate says can_run and Save is enabled, but publication refuses R_CONFIRMED_ON_CHANGED at repeats.rep1, because description_transition._dates compares descriptions only. Unticking and ticking again gives today's date again, so there is no way out except changing the channel list or the files. The same flow saves on main 4e8e982 with the dates kept (reproduced with the first save dated 2026-01-01). It is fail-closed, so nothing refused before is now accepted, but it is a dead end that appears exactly when a user picks the correct fix. The spec has to settle this. Either a header-only channels_changed is not a reopening (the declared ordered channel list did not change, so §4.4a keeps the date), or publication must accept a new date for repeats that the reconciliation reopened. Either way it needs an owner decision and a test of the whole save path.
Other items: 2, 3, 4, 5, 6 and 7 are correct and their tests prove them. Every reopened() caller is updated. Spec modules plus test_parity_v0_4_0: 1673 passed, 0 skipped, no fixture or golden changes. python tools/check.py: ruff clean, 5069 passed, 4 skipped (file-system/permission skips).
Generated by Claude Code
Experiment-v1 section 10 reopens a repeat when "a unit's ordered channel list" changes: the declared list in the description, not the image headers. A header rename fixed through header_names (tagging the channel again) no longer drops the date, so it saves instead of being refused R_CONFIRMED_ON_CHANGED at publication. Headers that no longer fit stay the run's refusal (R_HEADER_CONTRADICTION), shown at once. - published_answers.reopened compares the candidate's layouts (units with their ordered channels and image type) with the published ones; reasked drops the date of a repeat an answer just re-laid-out (a complete, non-published layout), so "same way?" is asked again; ticking a repeat whose layout is as published takes the published date back (section 10). - check_candidate runs publication's transition checks with the same re-confirmations (run.description_file.transition_refusals), so Save is disabled, with the reason, for a candidate publication would refuse. - A replicate whose date differs from the published one is not folded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Brings the audit batch 33/34a/34b fixes and the pre-release follow-ups into the v0.5.0 release candidate, keeping the P21/P29 description_in_use wiring. Conflicts: - notebook_folder.py: the folder check passes both with_description (P21/P29) and bind_headers=True (audit batch 33). - cli_describe.py: the module docstring says both that describe reads image headers when it compares a published description and that a non-NFC folder label is refused; the imports of both sides are kept. - CHANGELOG.md: main's Unreleased entries are folded into the 0.5.0 section, after the P21/P29 fix; the 0.5.0 introduction names #254, #255 and #257. Also: the P30 test's answers (added on this branch) now answer the image type as "channels", since an unanswered image type is an open question since audit batch 33 (unit_image_type_unanswered). tools/check.py: ruff clean, 415 files formatted, 5105 passed, 4 skipped (environment), code rules passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
RELEASE_REF (and the test asserting it) now names 0ea3254, the merge of origin/main (#254, #255, #257) into claude/release-0.5.0, which carries the P21/P29 fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Only docs/conformance.md: the measured commit (ba415de, whose RELEASE_REF is the merge 0ea3254), the counts (5109 collected, 5105 passed, 4 skipped; Spec modules 1671 passed, 0 skipped), tools/check.py (415 files, 5105 passed), the P30 answers now stating the image type, and the fixes merged from main: audit batches 33/34a (#254), 34b (#255) and the pre-release follow-ups (#257). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
1. A header refusal lifted by an answer about another cell. Tagging any channel of any folder recomputed every label's header_names from every saved tag (channel_answers.synced). After images were rewritten with other header names (for example two channels swapped), tagging one channel of an unchanged folder made the R_HEADER_CONTRADICTION of the changed folders vanish: Check and save said the images agree, the save published it and the run wrote the table. Now a tag confirms the header name of the cell tagged and nothing else: synced() takes the cells of the answer, drops the names no tagged cell has any more and adds a name only for those cells. The refusal stays until the channel it names is tagged again (the resolution #257 documents). 2. A "same way?" kept for other folders or another layout in a draft. With nothing published, the answer saved in morphobricks.draft.json was kept when a folder joined or left the replicate before the next folder check (carry_over keeps repeats by label), and when a tag or an image type changed after it (reasked ran only against a published file); it was then published as the user's confirmation. Now the draft file's date is kept only when every unit the repeat has now has its whole saved channel list and type and no unit with a saved answer left it (published_answers.resumed), and an answer that changes a repeat's declared layout reopens it with or without a published file; a repeat tagged back to the layout it was answered for keeps its date. The names card says which replicate is asked again. Tests: tests/test_notebook_final_review.py (10; 8 fail before the fix), and two existing tests follow the new rule (the alias of a cell not tagged by the answer; "same way?" asked again after the image type of a folder changed). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Pre-release follow-ups from the reviews of #254 and #255 and the owner decisions of 2026-11-10. Based on main 4e8e982. I checked each item against main first and reproduced it with a regression test that fails on 4e8e982 and passes here.
reopenedcompared the member folders only, at the reopen, and no answer re-checked it, so a re-ordered unit kept its saved date).confirmed = false) only when channels, layout or grouping change: a new or removed unit or member, a unit's ordered channel list, or the pairing of folders into repeats." The candidate's layout (units, their ordered channel IDs and image type) is compared with the published one: at the reopen (published_answers.reopened) and whenever an answer changes it (reasked, via the described run's_answer). Only that repeat'sconfirmed_onis dropped; ID and shown-as name are kept; every other repeat keeps its date byte for byte (§4.4a). Headers that no longer fit are not a reopening: they are the run's refusal.save_row = HBox([save_button, save_note]). When the description can be saved butrun_refusalsis non-empty,run_stop_htmlshows "You can save, but the run (step 12) will stop. Refused: R_HEADER_CONTRADICTION (2x). Each reason is listed above, with the folder it names." Nothing is shown while the checks wait for the axis roles._preview_drawnshow_preview()setpreview_checked.disabled = Falseeven when no plane was drawn, and a preview that failed later left the tick enabled and ticked._render_previewnow always ends in_gate_preview_tick(). The tick is enabled only when_preview_of(self.mapping)holds: a plane drawn with exactly the current roles, as the one-click_quick_preview_shown(now_preview_of(offer)) requires. A failed or stale preview takes an earlier tick back. Confirmed roles stay locked.describeprinted "No change." while the notebook reopened rep1.cli_describe._publishedreads the headers of the images to analyse (inspect_images, no pixel read) and callsobserve_folders(discovery, excluded, headers), aspublished_statedoes. It also reportsheaders changed: …and giveschannels_changed/renamed_folderreasons in words. The docstring and the user guide are updated.channel_stripand the native cropheader_preview) had the same before-only check, so it is fixed here too.check_unchangedruns after the read, with the fingerprint taken frompreview.checked_fingerprint(inchannel_stripagainst the fingerprint taken before the read).unit_layoutsgave a unit with a 3-channel and a 2-channel imagechannels == 3, and_channel_countspassed that on. In the notebook the experiment-wide axis check (different_channel_counts) blocks the axes today, but the gate no longer relies on that.UnitLayout.counts_agreeis new, and so ischannel_units.unit_channel_counts. A unit whose images disagree in count, or one of whose images has no known count, is left out, so the to-do showsunit_channel_count_unknown(its wording now says "every image … has the same number of channels"). Images that agree in count but not in names keep their count._saved_statussays "can start" only when_run_allowed()holds (the checks ran and passed). Before the axis roles are confirmed it says "Saved …. Confirm the axis roles (step 2) first…".Review of f8b57e3: the blocker and its fix (owner decision: follow §10 literally).
The vendored
tests/spec_fixtures/docs/experiment-v1.mdsays:"A unit's ordered channel list" is the
channelsarray of channel IDs in the description. So:DNA.header_names) leaves the list unchanged: the date is kept, Save is enabled and publication accepts it; the run passes. On f8b57e3 this was the dead end (R_CONFIRMED_ON_CHANGED at repeats.rep1).R_HEADER_CONTRADICTIONin Check and save as soon as the axis roles are confirmed; before them, the reviewer's "blocked by both" line with the header change). Nothing is hidden, nothing is ticked for the user.description_transition._datesalready agrees with §10: it refuses a new date only whenrepeat_layout(which holds each unit's ordered channels and image type) is unchanged, so the new date of the reopened repeat is accepted and the other repeat keeps its date. But re-ordering also moves channel positions, which §4.6 (c) makesR_CONFIRMED_ID_CHANGEDfor each channel moved ("some member folder, position or alias it held is removed or reassigned"), accepted only with an explicit recorded re-confirmation. The notebook makes such records only for removals and renames, so this candidate is refused, and Save is now disabled with that reason (on f8b57e3 it was enabled, then refused at publication, reproduced). The test shows that publication, given the §4.6 records, accepts the new date for that repeat only. A notebook flow to record a channel re-confirmation is a separate owner decision.run.description_file.transition_refusals, the same function_comparenow uses, with the same re-confirmations as_publish). Save's enabled state matches what publication accepts. A refused date names its way out: answering "same way?" again for a repeat whose layout is as published takes the published date back (§10: "copied from the published file, never from the clock"), only on the user's click. Such a replicate is not folded away.Behaviour change to note: on a reopen after a header-only change, the replicate keeps its date and its toggle stays ticked;
R_HEADER_CONTRADICTIONis shown at once. The command line'sdescribestill reports the folder reconciliation'schannels_changedas "must be confirmed again" and--publishrefuses (fail-closed; its wording could follow in a later PR).API:
published_answers.reopened(draft, answers, published)(thereconciliationargument of a75915f is gone again);published_answers.layouts,published_layouts,reasked,kept_date;run.description_file.transition_refusals;description_candidate.transition_issue;check_candidate(..., published=None, confirmations=()).UnitLayout.counts_agreeandunit_channel_countsare new.Science rules: no answer that was refused before is accepted now. Nothing is confirmed automatically (a tick is only ever taken back; a date is set only by the user's click, and for a repeat that is not reopened that click keeps the published date). No refusal is hidden: Save is now disabled, with the reason, for everything publication refuses, and the run's refusals are shown at once. Nothing is guessed (a count that cannot be known stays unknown).
Release branch: the P21/P29
with_descriptionwiring is not touched. A trial merge oforigin/claude/release-0.5.0(76e09c3) on top of this branch (re-checked on 789e2bb) conflicts inCHANGELOG.md, in thecli_describe.pymodule docstring (both rewrite the "reads the folder structure" sentence, an easy resolution) and innotebook_folder.py, which this PR does not touch (that conflict is between main and the release branch).notebook_check_save.pyauto-merges.Verification (head 789e2bb)
python tools/check.py: Ruff, format and code rules clean; 5074 passed, 4 skipped (all 4 are file-system or permission skips: case-insensitive names x3, root ignores file modes x1).test_parity_v0_4_0: 1673 passed, 0 skipped, no golden or fixture changes.test_a_a_rename_resolved_through_header_names_keeps_the_dates_saves_and_runs, (b)test_b_a_swap_resolved_by_the_unit_s_channel_order_reopens_only_that_repeat, (c)test_c_a_date_publication_would_refuse_never_reaches_a_save_click, (d)test_d_a_header_change_shows_its_refusal_at_once_and_reopens_nothingplus the reviewer's two row-fold tests (updated: the refusal is still visible at once, without a re-ask);test_published_answersgains three library tests ofreopened/reasked/kept_date.embed_minimal_htmlin Playwright Chromium (1180 px, deviceScaleFactor 2): (a) rename resolved, dates kept, Save enabled; (b) re-ordered, re-asked, then answered with Save disabled byR_CONFIRMED_ID_CHANGED; (c) Save disabled byR_CONFIRMED_ON_CHANGEDwith its way out; plus the earlier cards.Generated by Claude Code