Repository navigation
Conversation
AnVIL's anvil_activity names the child's biosample for 1,314 ANVIL_HPRC files of the child's parents, so their samples conflicted and they named no one. A curator rule (rules/curator_rules.yaml, contract 1.6, 4.7) now answers such a conflict with the samples the sample table's maternal_id or paternal_id and the file's read group or name agree on; the conflict stays listed with every source, outcome answered and answered_by the rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Filename-only confirmation can incorrectly assign every member of a multi-valued cell; it must require a singleton unless content confirms the complete set.
1 open issue
What changed in this PR
Adds the first curator rule to Meta-disco’s reconciliation pipeline, resolving eligible HPRC parent-read sample conflicts while preserving competing evidence for review.
Changes:
- Introduces validated curator rules with filename or content confirmation.
- Applies answers during sample reconciliation and records rule attribution and provenance.
- Updates reports, schema descriptions, documentation, and tests for answered conflicts.
| File | Description |
|---|---|
tests/test_rules_report.py |
Checks curator rules appear as declared sample rules. |
tests/test_reconcile_samples.py |
Updates fixtures for required curator-rule input. |
tests/test_reconcile_report.py |
Adjusts expected sample outcome columns. |
tests/test_curator_rules.py |
Tests rule validation, confirmation, and exclusions. |
tests/test_code_rules.py |
Checks curator rule IDs for collisions. |
tests/test_activity_samples.py |
Tests answers, propagation, and retained diagnostics. |
src/meta_disco/schema/classification.yaml |
Documents curator-rule attribution. |
src/meta_disco/schema/classification_model.py |
Regenerates attribution descriptions. |
src/meta_disco/rules/curator_rules.yaml |
Declares the HPRC parent-read rule and supporting citations. |
src/meta_disco/reconcile.py |
Loads curator rules and records their digest. |
src/meta_disco/reconcile_samples.py |
Applies answers and retains competing sample sources. |
src/meta_disco/curator_rules.py |
Defines and validates curator rules and filename checks. |
scripts/generate_rules_report.py |
Includes curator rules in the rule inventory. |
scripts/generate_reconcile_report.py |
Reports answered outcomes and rule IDs. |
docs/claims-contract.md |
Documents the limited curator-rule implementation. |
docs/anvil-data-issues.md |
Describes resolved HPRC cases and remaining conflicts. |
CLAUDE.md |
Documents curator-rule behavior and attribution. |
Review effort: Balanced
A name names one sample, so a curator rule's name check now holds only where the agreeing sources name exactly that sample (or a declared pair's sample for it); a cell naming several needs the read group. Raised by Copilot on #785. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The override changes sample and donor identity, requiring validation of the claimed 1,310 answered files, retained exceptions, and donor reachability against the anvil15 corpus beyond source inspection alone.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
… false (#777) From the no-context adversarial review: the loader refuses a rule whose two pairs share one name; tests cover an incomparable activity, two rules sharing one column, and reconcile reading the bundled rules by default. The schema's samples description, CLAUDE.md, contract 5.6 and comments that said differing sources always give none now say a curator rule may answer them; "read group" is widened to the file's own content (its header). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
ANVIL_HPRC: 1,310 files answered, 4 conflicts and 1 incomparable left, unreached donors 108 to 10; three sample records added (giab:HG004, HG006, HG007). No dimension's count moved against 20261109_222451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
…ocstrings (#777) From the final no-context review: the loader checked a rule's columns against every cell the slot map reads, so a column read only for another slot loaded as a rule that never answers; `SlotMap.cells` takes a slot, and the loader asks for the samples cells. Tests cover that, `samples` or `same_sample` not a list, and empty `columns`. `_cells_of` no longer repeats the dataset check `answering_rule` makes. The module, `answer` and `answering_rule` docstrings and the schema's `named_by` and `Attribution` descriptions now say what an answered file's `named_by` holds and where answering is limited to a conflict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
4 tasks
The run matches 20261110_233353 in every count; only the run and the commit differ. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
…le (#777) Evidence whose envelope names no target dataset joins every dataset's files (reconcile's evidence join), so a cell of another dataset's sample table could reach an ANVIL_HPRC file and authorize the override. 17505ed removed this check as a duplicate of the file's dataset check; it was not. A regression test gives the file a cell from another dataset and expects no answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
The run matches 20261111_001341 key for key in its reconcile report: no file in the corpus has a sample-table cell from another dataset, so restoring the check moves nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
This branch has not been deployed
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.

What changed
src/meta_disco/rules/curator_rules.yaml(new): the first curator rule (contract 1.6, 1.7, 4.7). Its one row,hprc_parent_reads_named_by_sample_table, answers an ANVIL_HPRC samples conflict. It records its author, date, rationale and the two cited GIAB pairs.src/meta_disco/curator_rules.py(new): loads and checks the rules. It refuses:samplesin that dataset and table (SlotMap.cellsnow takes an optional slot);src/meta_disco/reconcile_samples.py:answering_rulejudges, once per file and from the file's own sources, which rule would answer it. Each table source must be a cell of the rule's own dataset, table and columns. Checking the file's dataset alone is not enough: evidence that names no target dataset joins to files in every dataset.answergives the answer each time the file settles. It sets the activity aside and settles the rest, so an agreeing parent or PLINK fileset keeps the answer.source_type: wrangler_annotation). It is countedanswered, and it stays insample_conflicts.ndjson.gzwith every source,outcome: answeredandanswered_bythe rule.src/meta_disco/reconcile.py: loads the rules, passes them toSampleReport(now a required argument) and recordscurator_rules_sha256in the envelope and the report.scripts/generate_reconcile_report.py: anansweredcolumn and its definition in the samples table, and ananswered_bycolumn indocs/sample-conflicts.tsv.scripts/generate_rules_report.py: curator rules are listed among the sample rules. Otherwise the rule would show as "undeclared".Attribution.rule_idcovers a curator rule; the generated model is regenerated.docs/claims-contract.md's "What is not true yet" now says one curator rule exists, for samples, and 5.6 is updated to match.CLAUDE.mdgains a bullet on curator rules.docs/anvil-data-issues.mdissue 1 records the effect.tests/test_curator_rules.py(new);tests/test_activity_samples.py;Why
HPRC's
sampletable has one row per assembled child. The row lists the parents' Illumina reads inmat_ilmnandpat_ilmn, besidematernal_idandpaternal_id, and the parents have no row of their own. AnVIL makes oneanvil_activityfrom each row, and itsused_biosample_idis the child's biosample for every file in the row, the parents' reads included. So 1,314 parent files had a source naming the child against the table and, for the CRAMs, the read group naming the parent. Each was a conflict with no samples, which left their donors unreached. Examples from anvil15:HG01047.final.cramHG00735pat_ilmn→paternal_idHG01047NA24695_CTTGTA_L002_R1_034.fastq.gzHG005mat_ilmn→maternal_idHG007HG003_HiSeq30x_subsampled_R2.fastq.gzHG002pat_ilmn→paternal_idHG003The contract lets only a curator rule answer a conflict (4.7), and the conflict stays recorded. The table is supporting evidence, not the decider. The rule answers only where the file's own read group or its name names the same parent:
NA24694_*andNA24695_*. Those Coriell ids are GIAB's HG006 and HG007, per NCBI BioSample SAMN21924297 ("NIST HG006", cell line "NA24694 at Coriell", male) and SAMN21924298 ("NIST HG007", "NA24695 at Coriell", female).HG003_*andHG004_*.A file name is a check, never a source: the name never enters the samples or their
named_by, and only decides whether the rule applies.Assumptions I made
.craiwhose only own source is an activity naming the child, under an answered CRAM. Its own sources don't conflict, so no rule is judged for it. It then takes the parent from the CRAM, conflicts with its activity, and stays a conflict. No file in the run has this shape. Answering it would mean treating a parent's inherited samples as the table evidence the rule requires, which widens the rule. Dave decided to leave it.giab:HG007andcoriell:NA24695stay apart everywhere else.AGAACGAG-*,CTTGTCGA-*), and their four checksums, per Dave;HG005.paternal.GRCh38_no_alt.bam(incomparable), which is data issue 10.How to verify
Definition of done, from #777:
sample_conflicts.ndjson.gzno longer lists the pattern-1 files as unanswered conflicts. They are listedanswered(step 1). Per Dave's ground rule, this covers 1,310 files: the 4 barcode-named FASTQs stay conflicts.reconciled/sample_conflicts.ndjson.gz: runoutput/anvil/20261111_091236, classified and reconciled at d7861b3 with a clean tree, whose reports are committed in a1f5561. ANVIL_HPRC should show 1,310answered, eachanswered_byhprc_parent_reads_named_by_sample_table, plus 4conflictand 1incomparable. AnVIL_HPRC_R2 should show 2conflict. The previous run,20261109_222451, had 1,314 + 2 conflicts and 1 incomparable.reconciled/bam_classifications.ndjson.gz, find ANVIL_HPRC'sHG01047.final.cram(file_idabfd3a7d-9960-321b-85af-3100bad08d54; ANVIL_1000G_high_coverage_2019 has a file of the same name, whose sources all agree). Itssamples.list_idpoints to a list insample_lists.ndjson.gzwhose one member iscoriell:HG01047. Itsnamed_bylists the read group, thesample.paternal_idcell and the curator rule, and not the activity.reconciled/fastq_classifications.ndjson.gz,NA24694_GCCAAT_L002_R2_021.fastq.gz's list has one member,giab:HG006, named by thesample.paternal_idcell and the curator rule.docs/sample-conflicts.tsv, the twom54329U_*.5mc.hifi_reads.bamrows have outcomeconflictand a blankanswered_by.docs/anvil-data-issues.mdissue 2 records the finding.docs/reconcile-report.md, the samples table shows ANVIL_HPRC with 1,310 answered and 4 conflicts. Its "What no route ties" table shows ANVIL_HPRC's unreached donors falling from 108 to 10. The run adds three sample records,giab:HG004,giab:HG006andgiab:HG007; no dimension's count moved against20261109_222451.make test-allpasses, as domake lint-all(which includes the typecheck) andmake format-check. The rule's tests aretests/test_curator_rules.pyand theansweredtests intests/test_activity_samples.py.Closes #777
🤖 Generated with Claude Code
https://claude.ai/code/session_01VF7tbFa5ZTmYWZ6JWKiBBS
🐦 Built with cc-claude-tools