Skip to content

Experiment design: one strict reader for other bricks (v0.3.4) - #59

Merged
guijacquemet merged 5 commits into
mainfrom
claude/experiment-design-reader
Oct 31, 2026
Merged

guijacquemet merged 5 commits into
mainfrom
claude/experiment-design-reader

Conversation

@guijacquemet

@guijacquemet guijacquemet commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Core fix for v0.3.4 (owner decisions of 31 October 2026): one experiment-design reader for the other bricks, and typed checks on design edits. No version bump here.

Summary

  • design: new read_confirmed_design(path, *, expected_sha256=None) -> ConfirmedDesign and DesignCheckError (a ValueError carrying issues).
    • ConfirmedDesign gives path, sha256 (of the bytes parsed), design, read-only repeat_groups ({(condition, biological_repeat): repeat_group}, normalization's input), group_members() ({group: {condition: biological_repeat}}), conditions, controls and groups.
    • Error codes: missing_experiment_design, unreadable_experiment_design, invalid_experiment_design, repeat_group_conflict, design_not_confirmed, changed_experiment_design.
  • Typed edits (owner decision): both readers now check two values, read_experiment_design (step 1 and the CLI) and read_confirmed_design. A bad edit is refused with DesignFormatError naming the line and column, for example experiment_design.csv, line 2, column confirmed_on: '26/10/2026' is not a date in the form YYYY-MM-DD (for example 2026-10-26).
    • repeat_group follows D-e: not empty, na, nan, none, null, n/a or <na> in any case, and no outer whitespace. An empty group no longer means the folder name.
    • confirmed_on is empty or YYYY-MM-DD.
  • Designs built in code: design_issues reports the same values as invalid_experiment_design, so run_experiment stops before writing anything. write_experiment_design refuses to write them, so the brick only ever writes ISO dates; the notebook, CLI and run already fill dates with date.isoformat() (tested).
  • read_experiment_design stays lenient about the file's form (any-case booleans, BOM, outer spaces on other values).
  • Docs: contract §12 and §16 (new row), plan question 40, user guide codes, AGENTS.md, and the CLI's column help.

The two copies, and what is kept

Both copies call read_experiment_design and then design_issues, then compare file_sha256 with the manifest's hash.

# Difference CellPatcher load_experiment_design MorphoFeature ExperimentDesign.read (#110) Shared reader
1 Missing file its own missing_experiment_design generic "cannot read" missing_experiment_design
2 Errors ValidationIssues with codes text only issues with CellPatcher's codes, plus text
3 Caught DesignFormatError any ValueError the same refusals (OS errors are wrapped)
4 Expected hash from the manifest parameter parameter expected_sha256
5 Groups plain dict MappingProxyType read-only
6 Hash vs parse file read twice file read twice one read; hash of the parsed bytes
7 Grouping record none record() stays in MorphoFeature
8 Same-name repeats split across groups accepted refused at fit time (provider contract) not refused here: it would refuse valid files

Where both copies agree, the shared reader is stricter and fills nothing in. It refuses:

  • a byte-order mark;
  • a header that is not exactly the §12 columns in order, including duplicate or extra columns (the lenient reader kept the last duplicate);
  • ragged rows, unclosed quotes and blank lines;
  • outer whitespace in any value;
  • TRUE, yes, 1 or empty for the booleans;
  • non-canonical counts;
  • NA-like or case-clashing labels (D-e);
  • an empty design.

It also applies the two typed checks above. Everything the brick writes is accepted, including a spreadsheet edit once a run has written it back (tested).

Tests

  • tests/test_confirmed_design.py (40 tests), shared reader:
    • a run's own design read with the manifest hash;
    • a spreadsheet edit refused until the run rewrites it;
    • the hash is of the bytes read, and CRLF is accepted;
    • a missing file or a folder;
    • 31 refusal cases, including an n/a group, an empty group, and 26/10/2026 refused with the expected format in the message;
    • an unconfirmed group, a conflict, and the wrong hash;
    • every written design read back.
  • tests/test_design.py, typed edits:
    • NA-like groups (9 spellings) and groups with outer whitespace refused, naming line and column;
    • 26/10/2026 refused with the exact message;
    • other non-ISO dates refused;
    • the writer refuses a bad date or group and writes nothing;
    • a run with a bad date stops before writing;
    • the CLI dates a hand confirmation as YYYY-MM-DD.
  • On the old code: test_confirmed_design.py fails at collection (read_confirmed_design missing). Against the first commit of this PR, 24 of the typed-edit tests fail.
  • Gate (Python 3.13, pp2 venv, worktree with PYTHONPATH=src): ruff check . passed, ruff format --check . passed, pytest 1403 passed, 1 skipped (the root file-mode test test_discovery.py:665).

Behaviour change for users

A design file with an empty repeat_group, an NA-like group, or a date such as 26/10/2026 is now refused by the notebook and CLI, where before it was read (and the empty group filled with the folder name). Release note needed.

Follow-ups (other repositories, not edited)

🤖 Generated with Claude Code

https://claude.ai/code/session_01VbytvBRB9CmKQ5AAkhEFcv

read_confirmed_design(path, expected_sha256=) -> ConfirmedDesign replaces
CellPatcher's load_experiment_design and MorphoFeature's
ExperimentDesign.read. It reads the file as this brick writes it and
fills nothing in (exact header, one value per column, no outer
whitespace, true/false only, no empty repeat group or count, D-e labels,
no empty design), applies design_issues, checks the manifest's SHA-256,
and hashes the bytes it parsed. Refusals raise DesignCheckError with
ValidationIssues (missing_, unreadable_, changed_experiment_design,
repeat_group_conflict, design_not_confirmed). Contract §16, plan
question 40.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbytvBRB9CmKQ5AAkhEFcv
Owner decision of 31 October 2026: when the user's edits to
experiment_design.csv are read, repeat-group names follow the D-e
identity rules (no NA-like value or empty, no outer whitespace) and
confirmed_on is empty or an ISO date (YYYY-MM-DD). Both readers refuse a
bad edit naming the line and column; an empty repeat_group no longer
means the folder name. design_issues gives invalid_experiment_design for
a design built through the API, so a run stops before writing, and
write_experiment_design refuses to write such values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbytvBRB9CmKQ5AAkhEFcv
Also tells CLI users that repeat_group must not be empty or NA and that
confirmed_on is YYYY-MM-DD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbytvBRB9CmKQ5AAkhEFcv
Contract §16 said read_confirmed_design applies the D-e case rule to
repeat_group, but neither reader nor design_issues checked it, so Rep1
and rep1 were read as two repeat groups. Both readers now refuse it
naming both lines and the column; design_issues reports it as
invalid_experiment_design and the writer refuses it.

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

Copy link
Copy Markdown
Contributor Author

Review (independent, from GitHub's current state at e703951).

Fixed, 27b9f61: contract §16 and the read_confirmed_design docstring said repeat_group values differing only in case are refused (D-e), but neither reader nor design_issues checked it. check_identity_case covered only condition and biological_repeat, so Rep1 and rep1 were read as two repeat groups. The fix:

  • Both readers now refuse it, naming both lines and the column (line 3, column repeat_group: 'rep1' differs only in case from 'Rep1' on line 2).
  • design_issues reports it as invalid_experiment_design, and the writer refuses it.
  • New regression tests in test_design.py (lenient reader, design_issues and writer) and test_confirmed_design.py (strict reader). Both fail without the fix.
  • Contract §12, plan question 40 and the user guide are updated.

Checked, no change needed:

  • An empty or NA-like repeat group, or one with outer whitespace, is refused naming the line and column.
  • confirmed_on must be YYYY-MM-DD: 20261026, 2026-10-1 and 2026-02-30 are refused.
  • The strict reader refuses a BOM, header order, ragged rows, an unclosed quote, a blank line, TRUE/yes, and +2/007.
  • The SHA-256 is of the bytes parsed, and expected_sha256 is checked.
  • repeat_groups is read-only.
  • A run with a design that has invalid values stops before writing.
  • Tests against old main: all 20 new typed-edit tests in test_design.py fail there.

Follow-up (pre-existing, not changed here): when the notebook cannot read a saved design, _open_design falls back to the default and calls _save_design straight away, so the user's earlier answers are overwritten before they answer anything (the note says "Answering replaces the file"). This PR makes an unreadable file more likely: a spreadsheet program that saves 2026-10-26 as 26/10/2026 now makes the whole file unreadable.

Gate: main merged in (4331827, after #58). Ruff check and format are clean. 1460 passed, 1 skipped (test_discovery.py:665, file modes ignored as root).


Generated by Claude Code

@guijacquemet
guijacquemet marked this pull request as ready for review October 31, 2026 14:43
@guijacquemet
guijacquemet merged commit af33b5e into main Oct 31, 2026
7 checks passed
guijacquemet added a commit that referenced this pull request Oct 31, 2026
Version 0.3.4: declared axes and one design reader (PRs #58, #59)
@guijacquemet
guijacquemet deleted the claude/experiment-design-reader branch November 1, 2026 18:05
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.

2 participants