Skip to content

Release candidate v0.5.0: Gate G1 record, version 0.5.0, notebook installs v0.5.0 - #253

Merged
guijacquemet merged 14 commits into
mainfrom
claude/release-0.5.0
Nov 10, 2026
Merged

guijacquemet merged 14 commits into
mainfrom
claude/release-0.5.0

Conversation

@guijacquemet

@guijacquemet guijacquemet commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

What

Fourteen commits on main at bf733ee, with main at 8c573d6 merged in:

  1. 3b616a0 Prepare v0.5.0. pyproject.toml version 0.5.0 (static version, not derived from tags); the CHANGELOG's "Unreleased" becomes "0.5.0 - 2026-11-09" with a short release paragraph; the notebook's current_version = "0.5.0" and RELEASE_TAG = "v0.5.0"; notebooks/notebook_latest_versions.yaml 0.5.0; README install pins @v0.5.0; tests/test_notebook_setup.py versions.
  2. c74d717 Point the notebook at the v0.5.0 preparation commit. RELEASE_REF = "3b616a0a8791a0a93d5059e96c4cf311f717724d", the same two-commit pattern as v0.4.0 (4b8a32e, e77830a). A commit cannot hold its own SHA, so the notebook installs the preparation commit, whose code is the release's.
  3. 4437067 Gate G1 record in docs/conformance.md (the conformance plan names no place; the file already holds the conformance records), and the PR 17 run restored to the pin it ran with. Fixes docs/conformance.md was relabelled to Spec c8f6852 without a new conformance run #235.
  4. a6a0343 G1 gap tests (tests only). P06, P30, P34/P35 on the named fixtures, P28 on v09, P33 against t12, the section 2 confirmed_on frozen-clock test and the v27 digest test.
  5. c675f13 G1 record update (only docs/conformance.md).
  6. 0a2e25b Fix P21/P29: raise R_FOLDER_NOT_NFC and R_MASK_TYPE_NAME_INVALID on the described paths, with regression tests and a CHANGELOG note (details under Gate G1 below).
  7. 5cc3b07 Point the notebook at the fix commit. RELEASE_REF = "0a2e25b4ff4dd1f638ac65c50e09b43182e71a6d" (and the test asserting it), as c74d717 did for 3b616a0.
  8. 76e09c3 G1 record update (only docs/conformance.md): P21 and P29 met, measured on 5cc3b07.
  9. 0ea3254 Merge origin/main (8c573d6: Audit batches 33, 34a, 34b: publish only what was answered, of the image checked #254, Batch 34b: preview-gated one-click axes, header evidence on reopen, NaN crop #255, Pre-release follow-ups to #254 and #255 #257). Conflicts resolved so both behaviours are kept: notebook_folder.py passes with_description (P21/P29) and bind_headers=True (batch 33) to the folder check's preflight; cli_describe.py's docstring says both that describe reads headers when comparing a published description and that a non-NFC label is refused (both sides' imports kept); CHANGELOG.md folds main's Unreleased entries into the 0.5.0 section, after the P21/P29 entry. One test-only adjustment: the P30 test's answers (added on this branch) now state the image type "channels", since batch 33 makes an unanswered image type an open question (unit_image_type_unanswered).
  10. ba415de Point the notebook at the merge. RELEASE_REF = "0ea3254418fb808feddb0a235fabc025bd855e9e" (and the test asserting it).
  11. 43d1257 G1 record update (only docs/conformance.md): measured on ba415de, with the fixes merged from main listed.
  12. fd7afd3 Final review of v0.5.0: two confirmations the user did not give (details under "Final-review fixes" below), with tests/test_notebook_final_review.py (10 tests; 8 fail before the fix) and a CHANGELOG entry.
  13. 1e5390c Point the notebook at the final-review fix. RELEASE_REF = "fd7afd339bcaab7579c806a28a65bb7ecebe940c" (and the test asserting it).
  14. 18673aa G1 record update (only docs/conformance.md): measured on 1e5390c, with the final-review fixes listed.

git diff fd7afd3 HEAD -- src pyproject.toml is empty, so RELEASE_REF (fd7afd3) installs exactly the package code of this head.

Final-review fixes (fd7afd3)

Found by the final adversarial review of the candidate, driving the notebook headless. Both let something the user did not confirm be published and run.

  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; names no tagged cell has any more are dropped. The refusal stays until the channel it names is tagged again (the resolution Pre-release follow-ups to #254 and #255 #257 documents).
  2. A "same way?" kept for other folders or another layout in a draft. With nothing published, the date saved in morphobricks.draft.json was kept when a folder joined or left the replicate before the next folder check, and when a tag or an image type changed after it; it was then published as the user's confirmation. Now it is asked again in both cases (published_answers.resumed; reasked works with or without a published file), the names card says which replicate is asked again, and a replicate brought back to the layout it was answered for keeps its answer.

Gate G1 record

Measured on 1e5390c (the code and tests of this head; the last commit changes only docs/conformance.md). Earlier measurements: a6a0343 (5028 passed, 1660 Spec) found P21 and P29 not met, which 0a2e25b fixes; 5cc3b07 (5045 passed, 1671 Spec) was before the merge of main; ba415de (5105 passed, 1671 Spec) was before the final review. Every P-scenario in G1 scope (P01-P23, P25, P26, P28-P30, P32-P35) still passes in this run. Spec pin PIN.toml: MorphoBricks-Spec 1bed024 (unchanged). The scope ruling, ADR 0001 GS1, was merged in the Spec as cdc1841. That commit changes only the ADR and docs/conformance-plan.md, so the pinned files are unchanged.

Result
Whole suite (1e5390c) 5119 collected: 5115 passed, 4 skipped, 0 failed, 0 xfailed
Spec modules tests/test_spec_*.py (JUnit run, 1e5390c) 1671 passed, 0 skipped, 0 xfailed across all 21 EXPECTED_MODULES (JUnit: no skipped, failure or error element in any Spec case)
python tools/check.py on 1e5390c Ruff clean, 416 files formatted, pytest 5115 passed, 4 skipped, code rules passed, exit 0

Not run (environment, outside the Spec modules; owner ruling): test_cli_log.py:165, test_folder_rules.py:201 and :229 (case-sensitive file system), test_discovery.py:665 (run as root).

New tests and what each shows

Item Test What it shows
P06 (v12) test_physical_check::test_p06_swapped_fluorophores_in_unnamed_units_pass_and_each_unit_needs_its_own_look Two unnamed units of the same shape, DrugA's planes swapped: no issue; the physical report equals the unswapped folder's; one layout group, not batch-confirmable; the preview shows each unit's own planes, labelled C0, C1
P06 (v12) test_spec_run_aliases::test_p06_two_unnamed_units_with_swapped_fluorophores_run_unrefused_on_the_confirmation The run is written with no issue. Its experiment_description equals the unswapped run's. Every image is listed under images_without_header_channel_names
P30 (v13) test_spec_derived_design::test_p30_the_derived_file_of_migrated_legacy_answers_differs_only_in_the_earlier_date (2 cases) Legacy v13 migrated and derived gives the legacy file (and golden) byte for byte. With rep1 dated 10-01 and 10-15, rep1 takes 10-15 and the note lists both. The derived file differs from the legacy file in the CTRL/rep1 date only, and rep2/rep3 are identical. No clock read
P34 (v01) test_spec_records::test_p34_v01_one_changed_count_appends_one_line_and_keeps_every_confirmed_on, test_p34_v01_a_removal_and_count_changes_write_removed_then_one_line_per_member One image_count_changed line. The file differs only in that image_count line, so confirmed_on is byte-identical. RR3 writes removed and then one count line per remaining member
P35 (v25, t07) test_spec_records::test_p35_t07_is_what_a_confirmed_removal_from_v01_publishes, ..._t07_without_the_recorded_removal_is_refused_and_writes_nothing, ..._v25_is_what_removing_the_last_members_of_drug_a_from_v01_publishes, ..._on_the_v25_base_a_removed_repeat_keeps_every_retired_id, ..._on_the_v25_base_an_unconfirmed_removal_writes_nothing A confirmed removal from v01 publishes t07 / v25 byte for byte, with the right removed lines. t07 without the removal gives R_CONFIRMED_ID_CHANGED and writes nothing. On the v25 base a removed repeat keeps used_ids, and an unconfirmed removal writes nothing
P28 (v09) test_spec_run_inputs_semantic::test_p28_on_v09_each_batch_of_masks_is_unchecked_until_the_mask_step_reads_it Each of three batches is not yet checked and A_MASK_UNCHECKED until the mask step reads it. Earlier batches stay checked
P33 (t12) test_spec_reconcile::test_p33_a_... (one more assertion) The hand-made attachment is t12 and gives exactly R_CONFIRMED_ID_CHANGED
§2 confirmed_on test_description_write::test_a_frozen_clock_moved_by_a_day_writes_identical_bytes_for_an_unchanged_model (+ tests/frozen_clock.py) With the clock frozen on 11-09 and then moved to 11-10, the canonical bytes of every valid fixture are identical and the writer reads the clock 0 times. Republishing v01 leaves the file byte-identical, with no record
§2 descriptive keys / digests (v27) test_description_write::test_editing_only_target_or_treatment_key_of_v27_changes_no_digest_and_no_grouping Editing only target/treatment_key of v27 leaves all four digests and the derived rows unchanged
Final review tests/test_notebook_final_review.py (10) A header refusal is lifted only by tagging the cell it names (4 tests, including swapped channels in a draft); "same way?" in a draft is asked again when a folder joined or left the replicate (resumed draft and new check), and when a tag or an image type changed after it; an unchanged resumed draft keeps every answer

The rows that GS1 keeps in G1 passed in this run: RGB (v28, v29, i100-i106, i119), mask fields (i111, i112), descriptive keys and digests (v27), and the i99 half of Provenance.

Scope and status

  • Deferred to the Stage 2 gate per ADR 0001 GS1 (MorphoBricks-Spec cdc1841): P24, P27, P31 and the lab-tool-links part of §2 Provenance.
  • P21 and P29: met on v01 and v07 since 0a2e25b. The defect: R_FOLDER_NOT_NFC and R_MASK_TYPE_NAME_INVALID came only from preflight(..., with_description=True), which no production caller passed. Where the Spec puts them: experiment-v1 §3.3 (a folder label read from disk that is not NFC is refused by preprocessing, never normalized or renamed), §8 and ADR 0001 AQ11 (an invalid mask type name is refused at discovery, never reaching E_ID_TEXT; a non-NFC one is R_FOLDER_NOT_NFC), §10 (an R_* conflict blocks publication), conformance plan §3 P21/P29, and owner decision Q1 (only when a description is in use). The fix is one wiring: run.preflight.description_in_use(root, output) (the output folder holds a published morphobricks.toml) is passed as with_description by run_experiment, prepare_mask_step (CLI masks) and the CLI's check/run. The notebook's folder check and mask re-pairing pass it, or True while the user describes. Check and save is blocked while a name is refused, and describe refuses a non-NFC folder label. Nothing is renamed.
    • Before (c675f13): v01 plus CTRL/Café (NFD) gives R_UNIT_NOT_DECLARED only. v07 plus masks NA, cells, Café gives R_MASK_TYPE_NOT_DECLARED only.
    • After: the same runs also give R_FOLDER_NOT_NFC (CTRL/Café), and R_MASK_TYPE_NAME_INVALID ( cells, NA) plus R_FOLDER_NOT_NFC (Café). The run is not written and the folders are unchanged.
    • New tests: 11 in test_spec_discovery, covering run_experiment, the mask step, the CLI check/run/masks/describe and description_in_use. 6 in test_notebook_name_refusals, covering the folder check, re-pairing, the save gate and a v0.4 notebook with a published file. All of them fail on c675f13 except those for undescribed paths.
    • Undescribed parity: I ran the same trees (NFD folder, the five mask folders, no published file) on c675f13 and on the fix. The run, mask step and check outputs are byte-identical (plane_histograms.csv, mask_relationships.json, experiment_design.csv, the reports), apart from the manifest's written_at and the manifest SHA-256 that follows from it. test_parity_v0_4_0 (24) passed.
    • One deliberate change: the CLI describe now exits 1 with R_FOLDER_NOT_NFC on such a folder, where it used to draft it ("no ID can be proposed (E_ID_NOT_NFC)"). describe drafts a description, so §3.3 applies.
  • Colab and JupyterLab browser check (docs/colab-release-check.md): owner, before tagging.

Pins and links naming the old tag

Place Change
notebooks/MorphoBricks_Preprocessing.ipynb current_version 0.4.0 → 0.5.0, RELEASE_TAG v0.4.0 → v0.5.0, RELEASE_REF 4b8a32e… → 3b616a0… → 0a2e25b… (the P21/P29 fix) → 0ea3254… (the merge of main) → fd7afd3… (the final-review fix)
notebooks/notebook_latest_versions.yaml 0.4.0 → 0.5.0
README.md Status "current release is v0.4.0" → v0.5.0; pip install …git@v0.4.0 → @v0.5.0 (Installation); notebook-extra pip install …git@v0.4.0 → @v0.5.0; "Planned work after v0.4.0" → v0.5.0
tests/test_notebook_setup.py expected versions, RELEASE_REF and "pinned v0.5.0 candidate"
Unchanged The README Colab badge links blob/main and names no tag. The v0.4.0 mentions in docs/migration-0.3-to-0.4.md, docs/colab-release-check.md ("release PR of v0.4.0"), docs/contract.md and tests/test_parity_v0_4_0.py are history or deliberate comparisons with v0.4.0

Install check. After the push I installed the notebook's target commit with pip install --no-deps --target … from git+https://github.com/CellMigrationLab/morphobricks-preprocessing@fd7afd339bcaab7579c806a28a65bb7ecebe940c. It reports Version: 0.5.0, and its package files are identical to this head's src/morphobricks_preprocessing (diff -r). The @v0.5.0 README pins cannot be installed until the owner publishes the tag.

For the owner

  • Merge with a merge commit, not squash or rebase, so that RELEASE_REF fd7afd3 stays reachable from main, as with v0.4.0.
  • No tag or release is created here.
  • On docs/conformance.md was relabelled to Spec c8f6852 without a new conformance run #235: the issue says the historical run used 2975e0e. The record was written in 7418da1, where PIN.toml and the record's own first text both name b197ac1. 2975e0e was the first relabel (c9d7847), so b197ac1 is the pin restored.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…n's pin

docs/conformance.md: the G1 record measured on c74d717 (Spec pin 1bed024):
the whole suite, the Spec modules with no skip, P01-P35 with their tests,
the golden CSVs, the notebook items, the four environment skips listed as
not run, and the gaps against the conformance plan. The PR 17 run of
8 November is restored to the pin it ran with (b197ac1).

Fixes #235

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…, confirmed_on, v27

Tests only; src and pyproject.toml are unchanged, so RELEASE_REF 3b616a0 stays valid.

- P06: two unnamed v12 units with physically swapped fluorophores run unrefused, the
  check and the run record cannot tell them from the unswapped folder, the layout group is
  not batch-confirmable and the preview shows each unit's own planes
  (test_physical_check, test_spec_run_aliases).
- P30: the v13 legacy answers, migrated and derived, give back the legacy file byte for
  byte; with two dates in rep1 the derived file differs only in the earlier date, and no
  clock is read (test_spec_derived_design).
- P34 on v01 and P35 on v25 and t07: a confirmed removal from v01 publishes exactly t07 or
  v25; removals from the v25 base; t07 without the removal is refused (test_spec_records).
- P28 on v09: each batch of masks is unchecked until the mask step reads it
  (test_spec_run_inputs_semantic).
- P33 (a): the hand-made attachment is the fixture t12 (test_spec_reconcile).
- Section 2 confirmed_on: a frozen clock moved by one day writes identical bytes and the
  canonical writer reads no clock (tests/frozen_clock.py, test_description_write).
- Section 2 descriptive keys and digests on v27 (test_description_write).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…inding

Measured on a6a0343 (tests only on top of 4437067; src and pyproject.toml equal to 3b616a0).
P06, P30, P34, P35, P28 (v09) and the section 2 confirmed_on and v27 rows now pass on the
fixtures the plan names. P24, P27, P31 and the lab-tool part of Provenance are deferred to the
Stage 2 gate per ADR 0001 GS1 (MorphoBricks-Spec cdc1841). P21 and P29 are recorded as not met
on v01 and v07: a run with a published description never reports R_FOLDER_NOT_NFC or
R_MASK_TYPE_NAME_INVALID (source defect, left for the owner). The Colab and JupyterLab browser
check is the owner's, before tagging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…paths (P21, P29)

The two refusals were raised only by preflight(..., with_description=True), and no
production caller passed it: a run with a published morphobricks.toml refused a decomposed
folder only as R_UNIT_NOT_DECLARED and the mask types NA, ' cells' and a decomposed name
only as R_MASK_TYPE_NOT_DECLARED.

Spec experiment-v1 section 3.3 (a folder label read from disk that is not NFC is refused by
preprocessing, never normalized or renamed), section 8 and ADR 0001 AQ11 (an invalid mask type
name is refused at discovery, never reaching E_ID_TEXT), section 10 (an R_* conflict blocks
publication) and owner decision Q1 (only when a description is in use).

- run.preflight.description_in_use: the output folder holds a published morphobricks.toml.
- run_experiment, prepare_mask_step and the command line's check/run pass it as
  with_description; the notebook's folder check and mask re-pairing pass it, or True while
  the user describes the experiment; Check and save is blocked while a name is refused.
- The command line's describe refuses a folder label that is not NFC.

Without a published description nothing changes. Regression tests on v01 and v07 through
run_experiment, the mask step, the command line and the notebook fail on c675f13.

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 0a2e25b, the commit that raises
R_FOLDER_NOT_NFC and R_MASK_TYPE_NAME_INVALID on the described paths.

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, the counts (5049 collected, 5045 passed,
4 skipped; Spec modules 1671 passed, 0 skipped), the P21 and P29 rows with the new tests,
and the summary against the conformance plan, with the undescribed-parity evidence.

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
RELEASE_REF (and the test asserting it) now names fd7afd3, the commit that
carries the two final-review fixes on top of the merge of main (0ea3254).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
The record of the release candidate v0.5.0 is measured again on 1e5390c (the final-review fix
fd7afd3 and the RELEASE_REF commit): 5115 passed, 4 skipped (environment, outside the Spec
modules), Spec modules 1671 passed with 0 skipped, tools/check.py exit 0. It lists the two
final-review fixes. Only this file changes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
@guijacquemet
guijacquemet merged commit 3f10545 into main Nov 10, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/release-0.5.0 branch November 10, 2026 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs/conformance.md was relabelled to Spec c8f6852 without a new conformance run

2 participants