Skip to content

Map Ribo-TISH's colon-qualified ORF types on their location - #12613

Open
FelixKrueger wants to merge 3 commits into
nf-core:masterfrom
FelixKrueger:fix/orfnormalise-composite-orf-types
Open

Map Ribo-TISH's colon-qualified ORF types on their location#12613
FelixKrueger wants to merge 3 commits into
nf-core:masterfrom
FelixKrueger:fix/orfnormalise-composite-orf-types

Conversation

@FelixKrueger

Copy link
Copy Markdown
Contributor

Ribo-TISH qualifies a positional ORF-type label after a colon rather than replacing it — Novel:CDSFrameOverlap, 3'UTR:CDSFrameOverlap, Internal:CDSFrameOverlap, Novel:Known. CLASS_TOKENS holds only the bare locations and classify() matched the whole token, so all of those fell through unmatched; since #12498 made an unmatched token fatal, real Ribo-TISH output now aborts the process. This matches on the part before the first colon instead. orf_class is positional and CDSFrameOverlap describes a frame relationship rather than a position, so the qualifier does not belong in the class — and orf_type_native already carries the full token verbatim, so nothing is lost. No new vocabulary entries are needed.

Found by four nf-core/riboseq pipeline tests failing on real chr20 data, with 3–33 offending rows each.

Verification notes (AI-assisted)

Why the existing tests didn't catch it. The module fixtures use bare tokens; only genuine Ribo-TISH output produces the composite form, so #12498 was 63/63 green while this was already broken for real data. The added test uses the four tokens observed in production and fails without this change with the same message the pipeline produced:

orfnormalise: unmapped ORF type value(s) for caller 'ribotish':
  "3'UTR:CDSFrameOverlap" (1), 'Internal:CDSFrameOverlap' (1),
  'Novel:CDSFrameOverlap' (1), 'Novel:Known' (1)

Why location-only, rather than enumerating the composites. Taking the location keeps orf_class purely positional, which is the invariant #12498 established when it split length out into is_smorf. Enumerating <location>:<qualifier> pairs would need a decision per combination and would reintroduce the brittleness that exact-token matching removed. The four observed tokens resolve through the existing table with no additions:

token location orf_class
Novel:CDSFrameOverlap Novel novel_u
Novel:Known Novel novel_u
3'UTR:CDSFrameOverlap 3'UTR dORF
Internal:CDSFrameOverlap Internal intORF

Why the split is safe for every caller. Checked all 52 CLASS_TOKENS keys across the five callers: none contains a colon, so splitting cannot shorten a token that was meant to match whole. A comment on the table records that constraint for anyone adding keys later.

A location that matches nothing still fails, so the guard #12498 added keeps its value — only the qualifier is permitted to be unknown, which is what makes this robust to Ribo-TISH adding qualifiers.

nf-test test modules/nf-core/custom/orfnormalise — 11/11 pass locally (10 pre-existing plus the new one).

Ribo-TISH qualifies a positional label after a colon rather than replacing it:
Novel:CDSFrameOverlap, 3'UTR:CDSFrameOverlap, Internal:CDSFrameOverlap,
Novel:Known. CLASS_TOKENS holds only the bare locations and classify() matched
the whole token, so every one of those fell through unmatched. Since nf-core#12498 made
an unmatched token fatal, real Ribo-TISH output now aborts the process — four
nf-core/riboseq pipeline tests failed this way on chr20 data, with 3 to 33
offending rows each.

Match on the part before the first colon. orf_class is positional, and
CDSFrameOverlap describes a frame relationship rather than a position, so the
qualifier does not belong in the class; orf_type_native already carries the full
token verbatim, so nothing is lost and the harmonisation stays auditable. No
CLASS_TOKENS key contains a colon, so this cannot shorten a token meant to match
whole, and no new vocabulary entries are needed: the four tokens resolve to
novel_u, dORF and intORF through the existing table.

The module tests missed this because their fixtures use bare tokens; only real
Ribo-TISH output produces the composite form. The added test uses the four
observed tokens and fails without this change with the same message the pipeline
produced.
FelixKrueger added a commit to nf-core/riboseq that referenced this pull request Aug 6, 2026
The workflow is registered (id 328515623) from its 08-06 run, so the dispatch API
resolves it by numeric ID and workflow_dispatch is enough to start it on demand.
Dispatch by filename would not work: that lookup requires the file to be on the
default branch, and this one deliberately never reaches master.

The push trigger is dropped rather than kept alongside. Pushing this commit would
otherwise have had a chance of starting the four 45-minute jobs, and they cannot
pass until nf-core/modules#12613 has merged and the SHAs are re-bumped.
The original checks for this head wedged during the 2026-08-06 Actions incident:
queued with zero jobs created, and refusing both cancel ("already completed")
and rerun ("already running"). No API path revives them, so a new head is the
only way to get CI. The tree is unchanged, so the local 11/11 verification still
describes exactly this content.
@FelixKrueger

Copy link
Copy Markdown
Contributor Author

CI is red here but none of it belongs to this PR: all four failures are PIRATE, and they split by container engine — PIRATE passes on docker in this same run and fails only on conda and singularity. This PR changes Python inside a module template, which is engine-agnostic, so a failure that appears on two engines and not the third cannot be coming from it. On singularity the log says Failed to pull singularity image; on conda PIRATE runs but writes its own fail_test.txt instead of its expected outputs. Meanwhile CUSTOM_ORFNORMALISE ran in 33 shards and passed in every one, including the new test ribotish composite ORF types map on the location, keeping the qualifier native, and BWAMEM3_MEM and STRINGTIE_STRINGTIE passed alongside PIRATE in the same failing shards, so the runners are healthy. One other thing worth flagging: the head is 4a6b135f rather than d4d18fce because the original checks wedged during the 2026-08-06 Actions incident — that commit is empty and the tree is byte-identical. Happy to wait if you'd rather not merge past an unrelated red; nothing here needs a change on my side.

Evidence (AI-assisted)

The four failing checks, out of 72 check runs across 7 suites:

Check Cause
x64 | conda | 5 PIRATE snapshot mismatch
x64 | singularity | 5 PIRATE snapshot mismatch + Failed to pull singularity image
x64 | singularity | 6 same pair
confirm-pass-nf-test downstream aggregate, fails because the above did

x64 | docker | 5 and x64 | docker | 6 both passed — same shard numbers, so the same tests, on the third engine. That is the load-bearing observation.

PIRATE's diff shows the tool deciding it failed, not snapshot drift — expected outputs absent, failure marker present:

-    "PIRATE.gene_families.ordered.tsv",
-    "PIRATE.gene_families.tsv",
-    "PIRATE.pangenome_summary.txt",
-    "cluster_alleles.tab",
     "PIRATE.log",
+    "fail_test.txt",
     "genome2loci.tab",

This module's tests. Test Process CUSTOM_ORFNORMALISE appears in 33 shard jobs, every one with conclusion success and no FAILED lines. Confirmed by test name rather than by aggregate, since this repo shards at roughly one test per shard:

'ribotish composite ORF types map on the location, keeping the qualifier native'
'homo_sapiens [chr20] - ribotish'
'homo_sapiens [chr20] - ribotish + ribocode'
'homo_sapiens [chr20] - ribotish + ribocode - collapse disabled'
'homo_sapiens [chr20] - ribotish - score-field override'

Why the head moved. The five original checks on d4d18fce wedged during the Actions incident: queued with zero jobs created, refusing both recovery verbs with contradictory errors — cancel reported "already completed", rerun reported "already running" (403). With no API route to revive them, a new head was the only way to get CI. 4a6b135f re-parents the same tree (276fd8f25), so the content under test is unchanged and the local verification still describes it. Those five will read queued on d4d18fce indefinitely and gate nothing.

Local verification, unchanged from the PR description: 11/11 including a negative control — reverting only classify() makes the new test fail with the exact production error message.

@FelixKrueger

Copy link
Copy Markdown
Contributor Author

Merging master in a563217c cleared the PIRATE failures without touching orfnormalise — now green over 56 checks (50 passed, 6 skipped, none failed). So the attribution above is settled empirically too, and this is ready for review.

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.

1 participant