Skip to content

fix(eval): let --rcsb-pattern extract an id embedded in a longer folder - #365

Merged
marcuscollins merged 2 commits into
diff-use:mainfrom
manzuoni-astera:michaelanzuoni/patch-outputs-embedded-id
Aug 7, 2026
Merged

fix(eval): let --rcsb-pattern extract an id embedded in a longer folder#365
marcuscollins merged 2 commits into
diff-use:mainfrom
manzuoni-astera:michaelanzuoni/patch-outputs-embedded-id

Conversation

@manzuoni-astera

@manzuoni-astera manzuoni-astera commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

patch_outputs cannot process occupancy-sweep grid searches at all. Reported by @marcuscollins against the initial-40 protpardelle results.

What happens

Occ-sweep runs emit one folder per (entry, occupancy) pair — 1VME_0.25occA_0.75occB — and extract_rcsb_id required the id to fill the whole folder component. Every folder is skipped and the step fails. On the dataset in question that is 239 of 239 result folders; there are no bare-id folders at all.

Why no pattern worked around it

The whole-component rule was applied after the regex match, so:

  • capturing the id prefix (([0-9][A-Za-z0-9]{3})_[0-9.]+occ) → rejected, capture doesn't end at /
  • capturing the whole folder ((1VME_0.25occA_0.75occB)) → rejected, fails _VALID_RCSB_ID validation

I tested prefix-capture, whole-folder-capture and lookahead variants; all rejected. The docstring said "If your folders embed the id in a larger name, pass a custom --rcsb-pattern" — advice that could not be followed.

The change

The right-hand edge now accepts either the id filling the component or the pattern matching past its capturing group, which is the caller stating what follows the id:

--rcsb-pattern '<results-dir>/([0-9][A-Za-z0-9]{3}|pdb_[A-Za-z0-9]{8})_[0-9.]+occ'

Nothing follows the group in the default pattern, so the strict rule still applies there. The id must still start a component, so foo4hhb stays rejected.

Why the bare id is the correct answer here

Not a convenience: the inputs tree stores the reference at processed/1VME/1VME_single_001_density_input.cif — 42 bare-id folders, one shared by all five occupancy variants of that entry. PATCH_INPUT_PDB_PATTERN resolves against exactly that.

Verification

Ran the patched script inside the analysis env on the reporting workspace:

  • 1VME_0.25occA_0.75occB1VME, 9BN8_1.0occB9BN8, 1VME_1.0occA1VME
  • default pattern still rejects 4hhb_final, 1abc_pdb_1000abcd, 4hhb_0.5occA
  • default pattern still accepts 4hhb, pdb_00004hhb, 1VME

Two tests added: one for the delimiter-stating pattern, one asserting the default pattern's rejections are unchanged.

For the reporter

Until this merges, the same two-line edit to scripts/patch_output_cif_files.py in your synced checkout takes effect immediately under RUNTIME_PIXI=1, and you will need to set PATCH_RCSB_PATTERN to the occ-aware pattern above — the default cannot match your layout even with the fix.

Summary by CodeRabbit

  • Bug Fixes

    • Improved extraction of structure IDs from folder names.
    • Prevented default matching from incorrectly accepting IDs with trailing folder-name text.
    • Added support for custom patterns that explicitly define valid delimiters after an ID.
  • Documentation

    • Clarified ID matching behavior and added an occupancy-sweep pattern example.

patch_outputs could not process occupancy-sweep grid searches at all. Those
runs emit one folder per (entry, occupancy) pair — 1VME_0.25occA_0.75occB —
and extract_rcsb_id required the id to fill the whole folder component, so
every one was skipped and the step failed.

No --rcsb-pattern could work around it. The whole-component rule was applied
after the match, so capturing the id prefix was rejected for not ending at a
separator, and capturing the whole folder failed id validation. The docstring
told users to pass a custom pattern for exactly this case, which was advice
nobody could follow.

The right-hand edge now accepts either the id filling the component or the
pattern matching past its capturing group, which is the caller stating what
follows the id:

    --rcsb-pattern '<dir>/([0-9][A-Za-z0-9]{3}|pdb_[A-Za-z0-9]{8})_[0-9.]+occ'

Nothing follows the group in the default pattern, so the strict rule still
applies there and 4hhb_final is still skipped rather than silently patched as
4hhb. The id must still start a folder component, so foo4hhb stays rejected.

Extracting the bare id is correct here, not a convenience: the inputs tree
stores the reference at processed/1VME/1VME_single_001_density_input.cif, one
entry shared by all five occupancy variants.

Verified against the real dataset on diffuse-shared (239 result folders, all
occ-suffixed, zero bare ids) and by running the patched script inside the
analysis env, including every rejection case the docstring promises.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@manzuoni-astera, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eed88901-af8a-4e07-8edf-f3b3018e4a36

📥 Commits

Reviewing files that changed from the base of the PR and between a45d156 and 9f5daf9.

📒 Files selected for processing (1)
  • scripts/patch_output_cif_files.py
📝 Walkthrough

Walkthrough

extract_rcsb_id now supports IDs followed by regex-matched delimiters while preserving folder-boundary validation. Documentation and tests cover occupancy-sweep patterns and default-pattern rejection cases.

Changes

RCSB ID extraction

Layer / File(s) Summary
Delimiter-aware extraction logic
scripts/patch_output_cif_files.py
extract_rcsb_id accepts trailing folder content only when the regex matches an explicit delimiter. The documentation describes the rule and includes an occupancy-sweep example.
Extraction behavior tests
tests/eval/test_patch_output_cif_files.py
Tests cover custom delimiter patterns and confirm that the default pattern rejects embedded or suffixed folder IDs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: mag-astera, abdelsalam-abbas, denis-zaitsev

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enabling --rcsb-pattern to extract an ID embedded in a longer folder name.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes patch_output_cif_files.py’s extract_rcsb_id() so --rcsb-pattern can extract a valid PDB/RCSB id when it appears at the start of a folder name but is followed by additional, pattern-specified content (e.g., occupancy-sweep result folders like 1VME_0.25occA_0.75occB). This unblocks evaluation/patching workflows where results are organized as (entry, occupancy) folder pairs rather than bare-id folders.

Changes:

  • Relaxed the “whole component” right-edge rule to allow patterns that match past the capturing group (caller-stated delimiter) while still requiring the id to start a path component.
  • Expanded extract_rcsb_id() documentation to describe the delimiter-stating pattern approach for embedded ids.
  • Added tests covering delimiter-stating patterns and asserting the default pattern’s rejection behavior is unchanged.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
scripts/patch_output_cif_files.py Updates extract_rcsb_id() boundary checks to support embedded ids when the regex matches the delimiter after the capture.
tests/eval/test_patch_output_cif_files.py Adds regression tests for the new delimiter-stating behavior and for unchanged default-pattern rejections.
Suppressed comments (1)

scripts/patch_output_cif_files.py:199

  • Docstring still says "no complete PDB-id folder" / "when a whole component is captured". After the change, extraction can succeed when the id is embedded (delimiter-stating patterns), and InvalidRcsbIdError can be raised whenever the capturing group is not a valid id (not only when the whole component is captured). Updating this wording will keep the docstring consistent with the new behavior.
    Returns ``None`` when no complete PDB-id folder is found. Raises ``InvalidRcsbIdError``
    when a whole component is captured but is not a valid PDB id (a likely sign the pattern
    targets the wrong part of the path), and ``ValueError`` when the pattern does not have

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/patch_output_cif_files.py (1)

167-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use NumPy-style sections in the changed Python docstrings.

The repository guideline requires NumPy-style docstrings for every Python function.

  • scripts/patch_output_cif_files.py#L167-L169: Add Parameters, Returns, and Raises sections to extract_rcsb_id.
  • scripts/patch_output_cif_files.py#L183-L192: Keep the delimiter examples under an Examples section.
  • tests/eval/test_patch_output_cif_files.py#L60-L62: Add parameter and return sections to the updated test docstring.
  • tests/eval/test_patch_output_cif_files.py#L102-L113: Document the script fixture and None return.
  • tests/eval/test_patch_output_cif_files.py#L125-L130: Document the script fixture and None return.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/patch_output_cif_files.py` around lines 167 - 169, Update the Python
docstrings to NumPy style: in scripts/patch_output_cif_files.py:167-169, add
Parameters, Returns, and Raises sections to extract_rcsb_id; in
scripts/patch_output_cif_files.py:183-192, place delimiter examples under an
Examples section; in tests/eval/test_patch_output_cif_files.py:60-62, add
parameter and return sections; and at
tests/eval/test_patch_output_cif_files.py:102-113 and 125-130, document the
script fixture and None return.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/patch_output_cif_files.py`:
- Around line 193-195: Update the pattern_states_delimiter matching logic to
reject matches whose consumed suffix after group 1 crosses a folder boundary,
while still allowing delimiter text within the same path component and
preserving correct handling of multiple id-like substrings. Add a regression
test using a delimiter pattern that consumes “/” and verify it does not return
an ID from a later folder.

---

Nitpick comments:
In `@scripts/patch_output_cif_files.py`:
- Around line 167-169: Update the Python docstrings to NumPy style: in
scripts/patch_output_cif_files.py:167-169, add Parameters, Returns, and Raises
sections to extract_rcsb_id; in scripts/patch_output_cif_files.py:183-192, place
delimiter examples under an Examples section; in
tests/eval/test_patch_output_cif_files.py:60-62, add parameter and return
sections; and at tests/eval/test_patch_output_cif_files.py:102-113 and 125-130,
document the script fixture and None return.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ca603e7-9bba-4c13-8196-2034a4cfafae

📥 Commits

Reviewing files that changed from the base of the PR and between d69b062 and a45d156.

📒 Files selected for processing (2)
  • scripts/patch_output_cif_files.py
  • tests/eval/test_patch_output_cif_files.py

Comment on lines +193 to +195
Matching past the group is what distinguishes "I know what follows the id" from the
default pattern's "the id is the whole folder", so relaxing it here does not weaken the
default. Beware names containing more than one id-like substring.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

python - <<'PY'
import re

path = "/data/results/1VME_final/trial_1/refined.cif"
pattern = r"results/([0-9][A-Za-z0-9]{3}).*"
match = re.search(pattern, path)

assert match is not None
assert match.end(0) > match.end(1)
assert "/" in path[match.end(1):match.end(0)]
print("The current delimiter check accepts a match across '/'.")
PY

Repository: diff-use/sampleworks

Length of output: 214


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the referenced function and the relevant match logic without running repository code.
wc -l scripts/patch_output_cif_files.py
sed -n '160,240p' scripts/patch_output_cif_files.py

# Locate regression/test paths and any tests mentioning delimiter behavior.
fd -a 'test.*\.py|.*test.*\.py$|tests|conftest.py' . | sed 's#^\./##' | head -100
rg -n "pattern_states_delimiter|delimiter|patch_output_cif|1VME|cross|folder" -S .

Repository: diff-use/sampleworks

Length of output: 28627


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the existing delimiter-stating tests and any existing cross-boundary coverage.
sed -n '70,130p' tests/eval/test_patch_output_cif_files.py

# Programmatically model the current extract_rcsb_id boundary decisions for representative inputs.
python3 - <<'PY'
import re
from pathlib import Path

def current_end_ok(path: str, m) -> bool:
    pattern_states_delimiter = m.end(0) > m.end(1)
    return (
        pattern_states_delimiter
        or m.end(1) == len(path)
        or path[m.end(1)] == "/"
    )

cases = [
    ("/data/results/1VME/trial_1/refined.cif", r"results/([0-9][A-Za-z0-9]{3})"),
    ("/data/results/1VME_final/trial_1/refined.cif", r"results/([0-9][A-Za-z0-9]{3}).*"),
    ("/data/results/1VME_0.25occA/trial_1/refined.cif", r"results/([0-9][A-Za-z0-9]{3})_[0-9.]+occA"),
]
for path, pattern in cases:
    m = re.search(pattern, path)
    print(f"path={path}")
    print(f"pattern={pattern}")
    print(f"groups={m.groups()}")
    print(f"span match={m.span()} group={m.span(1)}")
    print(f"current_end_ok={current_end_ok(path, m)}")
    print(f"sliced_after_group={path[m.end(1):m.end(0)]}")
    print(f"crosses_slash_after_group={'/' in path[m.end(1):m.end(0)]}")
    print()
PY

Repository: diff-use/sampleworks

Length of output: 3885


Reject delimiter matches that cross a folder boundary.

pattern_states_delimiter accepts any text after group 1, so results/([0-9][A-Za-z0-9]{3}).* treats _final/trial_1/refined.cif as an explicit delimiter and returns 1VME. The documented delimiter is meant to describe what follows the id within the same folder component; a path separator should not be allowed as the delimiter. Require the consumed suffix to remain within the same folder component and add a regression test for a pattern that consumes /.

Suggested boundary check
-    pattern_states_delimiter = m.end(0) > m.end(1)
+    matched_suffix = path_str[m.end(1) : m.end(0)]
+    pattern_states_delimiter = (
+        bool(matched_suffix) and "/" not in matched_suffix
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/patch_output_cif_files.py` around lines 193 - 195, Update the
pattern_states_delimiter matching logic to reject matches whose consumed suffix
after group 1 crosses a folder boundary, while still allowing delimiter text
within the same path component and preserving correct handling of multiple
id-like substrings. Add a regression test using a delimiter pattern that
consumes “/” and verify it does not return an ID from a later folder.

The end_ok expression fits inside the 100-char limit, so ruff collapses
the hand-wrapped version. No behaviour change.
@marcuscollins
marcuscollins merged commit 16070a0 into diff-use:main Aug 7, 2026
12 of 15 checks passed
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.

4 participants