Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Predict RNA secondary structure, MFE folding, base-pair probabilities, RNA-RNA interactions via ViennaRNA Python bindings. Pipeline: sequence → MFE → partition function and pair-probability matrix → dot-bracket → duplex. Use for siRNA/sgRNA targeting, ribozyme design, RNA accessibility. Use RNAfold CLI for batch use without Python.
.claude/skills/jaechang-hits-viennarna-structure-prediction/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 282% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 162% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 235% | 0% |
| case-23 | ✗→✓ | ▲ Improved | 522% | 0% |
| case-22 | ✓→✗ | ▼ Worse | 598% | 0% |
ViennaRNA is the gold-standard toolkit for RNA secondary structure prediction based on thermodynamic nearest-neighbor parameters. It predicts the minimum free energy (MFE) structure and dot-bracket notation for a given RNA sequence, computes the full partition function to obtain base pair probabilities, and models RNA-RNA interactions via co-folding and duplex prediction. The Python bindings (import RNA) expose the full ViennaRNA C library with sequence-level and fold-compound APIs. Command-line programs (RNAfold, RNAalifold, RNAduplex) are also available and demonstrated here.
mfold (web server) or RNAstructure instead when you need Mfold algorithm predictions specifically or need the Efold partition function; ViennaRNA uses the Turner 2004 nearest-neighbor parameters and is the standard for research-grade thermodynamic predictionViennaRNA (Python bindings), matplotlib, numpybash# Install via conda (recommended) conda install -c conda-forge -c bioconda viennarna # Verify installation python -c "import RNA; print(RNA.__version__)" # 2.6.4 # Install additional Python dependencies pip install matplotlib numpy pandas # Optional: verify CLI tools are available RNAfold --version # RNAfold 2.6.4
pythonimport RNA # Predict MFE structure for an RNA sequence sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" structure, mfe = RNA.fold(sequence) print(f"Sequence: {sequence}") print(f"Structure: {structure}") print(f"MFE: {mfe:.2f} kcal/mol") # Sequence: GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA # Structure: (((((((..((((........)))).(((((.......))))).....(((((.......)))))))))))).... # MFE: -31.30 kcal/mol
Load an RNA sequence and compute its minimum free energy secondary structure using RNA.fold(). Validate the input and inspect the dot-bracket output.
pythonimport RNA def prepare_sequence(seq: str) -> str: """Normalize sequence: uppercase, replace T→U, validate alphabet.""" seq = seq.upper().replace("T", "U").strip() invalid = set(seq) - set("ACGUNX") if invalid: raise ValueError(f"Invalid characters in sequence: {invalid}") return seq # E. coli tRNA-Phe (GenBank: M10217) raw_seq = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" sequence = prepare_sequence(raw_seq) structure, mfe = RNA.fold(sequence) print(f"Sequence length: {len(sequence)} nt") print(f"Structure: {structure}") print(f"MFE: {mfe:.2f} kcal/mol") # Validate: structure length must equal sequence length assert len(structure) == len(sequence), "Structure and sequence length mismatch" # Count stems (paired bases) n_paired = structure.count("(") + structure.count(")") n_unpaired = structure.count(".") print(f"Paired bases: {n_paired} | Unpaired bases: {n_unpaired}") print(f"Stem fraction: {n_paired/len(sequence):.2f}")
The RNA.fold_compound object is the central API for partition function, base pair probabilities, and constrained folding.
pythonimport RNA sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" # Create fold compound (wraps the sequence with model parameters) fc = RNA.fold_compound(sequence) # Compute MFE structure via the fold compound API structure, mfe = fc.mfe() print(f"MFE structure: {structure}") print(f"MFE: {mfe:.2f} kcal/mol") # Evaluate free energy of an alternative structure alt_structure = "." * len(sequence) # fully unfolded energy = fc.eval_structure(alt_structure) print(f"Fully unfolded energy: {energy:.2f} kcal/mol") print(f"Folding stabilization: {energy - mfe:.2f} kcal/mol")
Compute the thermodynamic partition function to obtain ensemble-level base pair probabilities. High-probability pairs indicate well-defined structural elements.
pythonimport RNA import numpy as np sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" n = len(sequence) fc = RNA.fold_compound(sequence) # Step 1: MFE folding (required before pf for proper initialization) structure_mfe, mfe = fc.mfe() # Step 2: Rescale Boltzmann factors for numerical stability (optional but recommended) fc.exp_params_rescale(mfe) # Step 3: Compute partition function structure_pf, gibbs_free_energy = fc.pf() print(f"Gibbs free energy (ensemble): {gibbs_free_energy:.2f} kcal/mol") print(f"MFE structure: {structure_mfe}") print(f"Centroid (pf): {structure_pf}") # Step 4: Retrieve base pair probability matrix bpp = fc.bpp() # returns (n+1)x(n+1) matrix; 1-indexed # Convert to 0-indexed numpy array for analysis probs = np.zeros((n, n)) for i in range(1, n + 1): for j in range(i + 1, n + 1): if bpp[i][j] > 0.0: probs[i - 1][j - 1] = bpp[i][j] probs[j - 1][i - 1] = bpp[i][j] # Identify high-confidence pairs (p > 0.9) high_conf = [(i, j, probs[i, j]) for i in range(n) for j in range(i + 1, n) if probs[i, j] > 0.9] print(f"\nHigh-confidence base pairs (p > 0.9): {len(high_conf)}") for i, j, p in high_conf[:5]: print(f" {sequence[i]}{i+1} — {sequence[j]}{j+1}: p={p:.3f}")
Plot the base pair probability matrix as a heatmap to visualize structural regions.
pythonimport RNA import numpy as np import matplotlib.pyplot as plt import matplotlib.colors as mcolors sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" n = len(sequence) fc = RNA.fold_compound(sequence) structure_mfe, mfe = fc.mfe() fc.exp_params_rescale(mfe) fc.pf() bpp = fc.bpp() # Build matrix probs = np.zeros((n, n)) for i in range(1, n + 1): for j in range(i + 1, n + 1): if bpp[i][j] > 0.0: probs[i - 1][j - 1] = bpp[i][j] probs[j - 1][i - 1] = bpp[i][j] fig, ax = plt.subplots(figsize=(8, 7)) im = ax.imshow(probs, cmap="hot_r", vmin=0, vmax=1, origin="upper", aspect="equal") plt.colorbar(im, ax=ax, label="Base pair probability") ax.set_xlabel("Nucleotide position") ax.set_ylabel("Nucleotide position") ax.set_title(f"Base Pair Probability Matrix\n(n={n} nt, MFE={mfe:.2f} kcal/mol)") plt.tight_layout() plt.savefig("bpp_matrix.png", dpi=150, bbox_inches="tight") print("Saved: bpp_matrix.png")
Use RNA.cofold() to predict the interaction between two RNA sequences by concatenating them with an & separator.
pythonimport RNA # miRNA (hsa-miR-21-5p) and its target sequence in mRNA (PTEN 3'UTR region) mirna_seq = "UAGCUUAUCAGACUGAUGUUGA" target_seq = "UCAACAUCAGUCUGAUAAGCUA" # approximate complementary target # Co-fold: concatenate with & separator cofold_seq = mirna_seq + "&" + target_seq structure, mfe = RNA.cofold(cofold_seq) print(f"miRNA: {mirna_seq}") print(f"Target: {target_seq}") print(f"Co-fold MFE: {mfe:.2f} kcal/mol") # Parse the structure — & is retained in output n1, n2 = len(mirna_seq), len(target_seq) struct_mirna = structure[:n1] struct_ampersand = structure[n1] struct_target = structure[n1 + 1:] print(f"miRNA structure: {struct_mirna}") print(f"Target structure: {struct_target}") paired_in_duplex = struct_mirna.count("(") + struct_mirna.count(")") print(f"Bases paired across the duplex: {paired_in_duplex}")
Compute the accessibility of a target region within a longer mRNA sequence — critical for siRNA and antisense oligonucleotide efficiency.
pythonimport RNA import numpy as np def compute_accessibility(mrna_seq: str, window: int = 40) -> list: """ Compute per-position probability of being unpaired (accessible) using a sliding-window approach on the partition function. Returns list of (position, accessibility) tuples. """ n = len(mrna_seq) fc = RNA.fold_compound(mrna_seq) _, mfe = fc.mfe() fc.exp_params_rescale(mfe) fc.pf() bpp = fc.bpp() # Probability of being paired at each position p_paired = np.zeros(n) for i in range(1, n + 1): for j in range(1, n + 1): if i != j: p = bpp[min(i,j)][max(i,j)] p_paired[i - 1] += p p_unpaired = 1.0 - np.clip(p_paired, 0, 1) return p_unpaired # Example: 80-nt mRNA segment with a known accessible region mrna = "AUGCUAGCUAGCUAGCUAUGCUAGCUAGCUUUUUUUUUUUUAUGCUAGCUAGCUAGCUAGCUAGCUAGCUAGCUAGC" p_unpaired = compute_accessibility(mrna) import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(10, 3)) ax.bar(range(1, len(mrna) + 1), p_unpaired, color="#2166ac", alpha=0.8) ax.axhline(0.5, color="red", lw=1, ls="--", label="50% unpaired") ax.set_xlabel("Position (nt)") ax.set_ylabel("P(unpaired)") ax.set_title("RNA Accessibility Profile") ax.legend() plt.tight_layout() plt.savefig("rna_accessibility.png", dpi=150, bbox_inches="tight") print("Saved: rna_accessibility.png") # Top 5 most accessible positions (siRNA target candidates) best = sorted(enumerate(p_unpaired, 1), key=lambda x: -x[1])[:5] print("\nMost accessible positions:") for pos, prob in best: print(f" Position {pos}: P(unpaired) = {prob:.3f} ({mrna[pos-1]})")
Use the RNAfold CLI for batch folding via subprocess, then parse the output.
bash# Fold a single sequence from stdin echo "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" | RNAfold # Output: # GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA # (((((((..((((........)))).(((((.......))))).....(((((.......)))))))))))).... (-31.30) # Batch fold from FASTA file RNAfold < sequences.fasta > structures.txt # Generate base pair probability dot plot (PostScript) RNAfold --noPS < sequences.fasta # suppress PostScript output RNAfold -p < sequences.fasta # save dot plot as rna.ps
python# Python: run RNAfold via subprocess and parse output import subprocess import re def run_rnafold(sequence: str) -> tuple: """Run RNAfold CLI and return (structure, mfe) tuple.""" result = subprocess.run( ["RNAfold", "--noPS"], input=sequence, capture_output=True, text=True, timeout=30 ) if result.returncode != 0: raise RuntimeError(f"RNAfold failed: {result.stderr}") lines = result.stdout.strip().split("\n") # Last line: structure and energy, e.g. "((....)) (-5.40)" match = re.match(r"^([.()\[\]{}<>|]+)\s+\((-?\d+\.\d+)\)$", lines[-1]) if not match: raise ValueError(f"Could not parse RNAfold output: {lines[-1]}") structure = match.group(1) mfe = float(match.group(2)) return structure, mfe sequences = [ ("tRNA-Phe", "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"), ("miR-21", "UAGCUUAUCAGACUGAUGUUGA"), ] for name, seq in sequences: struct, mfe = run_rnafold(seq) print(f"{name}: {mfe:.2f} kcal/mol | {struct}")
Apply hard constraints (force or forbid specific base pairs) and enumerate suboptimal structures.
pythonimport RNA sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" # --- Constrained folding: force specific pairs --- fc = RNA.fold_compound(sequence) # Add hard constraint: force positions 1-7 to be paired (known stem) hc = RNA.hc_add_bp(fc, 1, 72) # force pair between position 1 and 72 (0-indexed in C API) structure_c, mfe_c = fc.mfe() print(f"Constrained MFE: {mfe_c:.2f} kcal/mol") print(f"Constrained struct: {structure_c}") # --- Enumerate suboptimal structures (delta_mfe threshold) --- fc_sub = RNA.fold_compound(sequence) _, mfe_opt = fc_sub.mfe() # Get suboptimal structures within 5 kcal/mol of MFE delta = 5.0 # kcal/mol window subopt_list = RNA.subopt(sequence, int(delta * 100)) # energy in 10-cal units print(f"\nSuboptimal structures within {delta} kcal/mol of MFE:") print(f"Total suboptimal structures: {len(subopt_list)}") for s in subopt_list[:3]: e = s.energy / 100.0 # convert from 10-cal to kcal/mol print(f" {s.structure} ({e:.2f} kcal/mol)")
| Parameter | Default | Range / Options | Effect | |-----------|---------|-----------------|--------| | sequence (RNA.fold) | — | ACGU string | Input RNA sequence; T is auto-converted to U | | temperature (model detail) | 37.0 °C | 0–100 °C | Folding temperature; lower temp stabilizes structures | | dangles (model detail) | 2 | 0, 1, 2, 3 | Dangling end treatment; 2=average, 0=none, use 2 for most applications | | noGU (model detail) | False | True/False | Disallow G-U wobble pairs when True | | noLP (model detail) | False | True/False | Disallow lonely base pairs (single-bp stems); reduces noise | | delta (RNA.subopt) | — | float kcal/mol | Energy window above MFE for suboptimal structure enumeration | | window (sliding window) | — | int nt | Sliding window size for long-sequence accessibility analysis |
When to use: Fold a library of RNA sequences (e.g., candidate aptamers, guide RNAs) and compare energies.
pythonimport RNA from pathlib import Path def read_fasta(fasta_path: str) -> list: """Parse FASTA file, return list of (name, sequence) tuples.""" records = [] name, seq = None, [] for line in Path(fasta_path).read_text().splitlines(): if line.startswith(">"): if name: records.append((name, "".join(seq).upper().replace("T", "U"))) name, seq = line[1:].split()[0], [] else: seq.append(line.strip()) if name: records.append((name, "".join(seq).upper().replace("T", "U"))) return records # Example: fold sequences from a FASTA file sequences = [ ("aptamer_1", "GGGUUUUGAAACUAAACUAGGCUCUAGCGCUGGUGUCCCUUCCCGGCUCUAGCCUCAGCAGAAGCUUGAAAAAACCC"), ("aptamer_2", "GGGAGACAAGAAUAAACGCUCAACGUCUACCAUGAUCGAAUGCUAGCCUUCUAGCUUGCUUCGGCAGCACUAUAGGG"), ("aptamer_3", "GGGCGACCCUGAUGAGUCCCAAGUCGAAACGAUUCCUUUUUAAACUCAUGGUGCCCAGCCUCGCUCAGCA"), ] print(f"{'Name':<15} {'Length':>8} {'MFE':>10} {'Structure'}") print("-" * 80) for name, seq in sequences: struct, mfe = RNA.fold(seq) print(f"{name:<15} {len(seq):>8} {mfe:>10.2f} {struct[:50]}...")
When to use: Evaluate whether a CRISPR sgRNA guide sequence folds into secondary structures that reduce Cas9 binding efficiency.
pythonimport RNA def assess_sgrna(guide_seq: str, scaffold: str = None) -> dict: """ Assess sgRNA secondary structure. guide_seq: 20-nt spacer sequence (RNA) scaffold: constant sgRNA scaffold sequence (default: SpCas9) """ if scaffold is None: # SpCas9 sgRNA scaffold (Addgene standard) scaffold = "GUUUUAGAGCUAGAAAUAGCAAGUUAAAAUAAGGCUAGUCCGUUAUCAACUUGAAAAAGUGGCACCGAGUCGGUGCUUU" full_sgrna = guide_seq.upper().replace("T", "U") + scaffold fc = RNA.fold_compound(full_sgrna) structure, mfe = fc.mfe() fc.exp_params_rescale(mfe) fc.pf() bpp = fc.bpp() n_guide = len(guide_seq) # Check if any guide bases are paired (bad for targeting) guide_paired = sum( bpp[min(i, j)][max(i, j)] for i in range(1, n_guide + 1) for j in range(1, n_guide + 1) if i != j ) guide_accessibility = 1.0 - min(1.0, guide_paired / n_guide) return { "guide_seq": guide_seq, "mfe": mfe, "structure": structure[:n_guide], "guide_access": guide_accessibility, "predicted_ok": guide_accessibility > 0.7, } guides = ["GCACUAGUGACGCAUGGCAC", "GGGCAUAGCUAGCUAGCUAU", "AAAUUCGCACUAGUGACGCA"] for g in guides: result = assess_sgrna(g) status = "OK" if result["predicted_ok"] else "WARN (self-paired)" print(f"{g}: accessibility={result['guide_access']:.2f}, MFE={result['mfe']:.1f} kcal/mol [{status}]")
When to use: Create a mountain plot — a classic RNA structure visualization showing stem height along the sequence.
pythonimport RNA import matplotlib.pyplot as plt def dot_bracket_to_mountain(structure: str) -> list: """Convert dot-bracket structure to mountain plot heights.""" heights = [] level = 0 for c in structure: if c == "(": level += 1 heights.append(level) if c == ")": level -= 1 return heights sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" structure, mfe = RNA.fold(sequence) heights = dot_bracket_to_mountain(structure) fig, axes = plt.subplots(2, 1, figsize=(12, 5), sharex=True, gridspec_kw={"height_ratios": [1, 3]}) # Top: sequence text axes[0].text(0.5, 0.5, "tRNA-Phe | E. coli", ha="center", va="center", fontsize=10, transform=axes[0].transAxes) axes[0].axis("off") # Bottom: mountain plot axes[1].fill_between(range(len(sequence)), heights, step="mid", color="#2c7bb6", alpha=0.7, label=f"MFE = {mfe:.2f} kcal/mol") axes[1].set_xlabel("Nucleotide position") axes[1].set_ylabel("Stem height") axes[1].set_title("Mountain Plot") axes[1].legend() plt.tight_layout() plt.savefig("mountain_plot.png", dpi=150, bbox_inches="tight") print(f"Saved: mountain_plot.png (MFE structure: {structure[:40]}...)")
| Output | Type | Description | |--------|------|-------------| | structure | string | Dot-bracket notation of MFE secondary structure; ( and ) for paired bases, . for unpaired | | mfe | float (kcal/mol) | Minimum free energy of the predicted structure; more negative = more stable | | bpp | (n+1)×(n+1) matrix | Base pair probability matrix from partition function; element [i][j] = P(i paired with j), 1-indexed | | bpp_matrix.png | PNG | Heatmap visualization of base pair probabilities | | rna_accessibility.png | PNG | Per-position probability of being unpaired | | mountain_plot.png | PNG | Mountain plot of stem heights along the sequence |
| Problem | Cause | Solution | |---------|-------|----------| | ImportError: No module named 'RNA' | ViennaRNA Python bindings not installed | Install via conda install -c conda-forge viennarna; pip install alone may fail to link C library | | RuntimeError: RNAfold not found | ViennaRNA CLI not in PATH | Confirm with which RNAfold; if using conda env, activate it before running scripts | | MFE is unexpectedly positive (> 0) | Very short or repetitive sequence with no favorable pairs | Short sequences (< 10 nt) often have positive MFE; check sequence length and composition | | bpp matrix all zeros after fc.pf() | fc.mfe() must be called before fc.pf() on the same fold compound | Always call fc.mfe() first, then fc.exp_params_rescale(mfe), then fc.pf() | | Suboptimal enumeration returns thousands of structures | Window too large for long sequences | Reduce delta to 2–3 kcal/mol for long sequences; very stable sequences have dense suboptimal ensembles | | Co-fold (RNA.cofold) shows unexpected pairing | Intramolecular folding dominates in one strand | Inspect each strand separately first; low individual-strand MFE indicates strong self-structure interfering with duplex |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 15,293 | 18,048 | +18% | 1 | 1 | 0% | 2,690 | 10,264 | +282% | 0 | 0 | — |
case-02 | fail→pass | 19,089 | 32,764 | +72% | 1 | 1 | 0% | 3,781 | 9,888 | +162% | 0 | 0 | — |
case-03 | pass→pass | 10,548 | 5,736 | -46% | 1 | 1 | 0% | 1,742 | 7,689 | +341% | 0 | 0 | — |
case-04 | pass→pass | 13,376 | 6,788 | -49% | 1 | 1 | 0% | 2,052 | 7,806 | +280% | 0 | 0 | — |
case-05 | fail→pass | 15,663 | 9,321 | -40% | 1 | 1 | 0% | 2,452 | 8,206 | +235% | 0 | 0 | — |
case-06 | pass→pass | 11,515 | 27,057 | +135% | 1 | 1 | 0% | 2,018 | 8,684 | +330% | 0 | 0 | — |
case-07 | pass→pass | 12,644 | 7,190 | -43% | 1 | 1 | 0% | 2,193 | 8,179 | +273% | 0 | 0 | — |
case-08 | pass→pass | 25,042 | 5,187 | -79% | 1 | 1 | 0% | 1,262 | 7,780 | +516% | 0 | 0 | — |
case-09 | pass→pass | 11,740 | 9,853 | -16% | 1 | 1 | 0% | 2,008 | 8,759 | +336% | 0 | 0 | — |
case-10 | pass→pass | 9,907 | 14,736 | +49% | 1 | 1 | 0% | 1,422 | 9,660 | +579% | 0 | 0 | — |
case-11 | pass→pass | 15,367 | 12,927 | -16% | 1 | 1 | 0% | 2,745 | 9,138 | +233% | 0 | 0 | — |
case-12 | fail→fail | 16,589 | 14,055 | -15% | 1 | 1 | 0% | 2,965 | 9,205 | +210% | 0 | 0 | — |
case-17 | pass→pass | 7,283 | 8,011 | +10% | 1 | 1 | 0% | 1,494 | 8,324 | +457% | 0 | 0 | — |
case-13 | pass→pass | 7,151 | 3,944 | -45% | 1 | 1 | 0% | 1,205 | 7,468 | +520% | 0 | 0 | — |
case-14 | pass→pass | 7,238 | 4,237 | -41% | 1 | 1 | 0% | 1,173 | 7,384 | +529% | 0 | 0 | — |
case-15 | pass→pass | 4,309 | 4,154 | -4% | 1 | 1 | 0% | 667 | 7,465 | +1019% | 0 | 0 | — |
case-16 | pass→pass | 11,558 | 9,705 | -16% | 1 | 1 | 0% | 2,045 | 8,587 | +320% | 0 | 0 | — |
case-18 | pass→pass | 3,574 | 3,097 | -13% | 1 | 1 | 0% | 628 | 7,285 | +1060% | 0 | 0 | — |
case-19 | pass→pass | 9,931 | 4,949 | -50% | 1 | 1 | 0% | 1,770 | 7,745 | +338% | 0 | 0 | — |
case-20 | pass→pass | 10,448 | 7,551 | -28% | 1 | 1 | 0% | 1,895 | 8,034 | +324% | 0 | 0 | — |
case-21 | pass→pass | 8,504 | 7,716 | -9% | 1 | 1 | 0% | 1,602 | 8,259 | +416% | 0 | 0 | — |
case-22 | pass→fail | 6,032 | 5,252 | -13% | 1 | 1 | 0% | 1,099 | 7,669 | +598% | 0 | 0 | — |
case-23 | fail→pass | 7,781 | 4,958 | -36% | 1 | 1 | 0% | 1,245 | 7,740 | +522% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +13 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.