From 9960b99adad4e98e4c7e344bc2f3864de8db41d6 Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Thu, 3 Sep 2026 11:31:51 -0700 Subject: [PATCH 01/13] CodonFM skills Signed-off-by: Ohad Mosafi --- requirements.txt | 6 +- skills/codonfm-embed/SKILL.md | 69 +++++ skills/codonfm-embed/agents/openai.yaml | 4 + skills/codonfm-embed/evals/evals.json | 29 +++ skills/codonfm-finetune/SKILL.md | 112 +++++++++ skills/codonfm-finetune/agents/openai.yaml | 4 + skills/codonfm-finetune/evals/evals.json | 40 +++ skills/codonfm-score/SKILL.md | 95 +++++++ skills/codonfm-score/agents/openai.yaml | 4 + skills/codonfm-score/evals/evals.json | 40 +++ skills/codonfm-setup/SKILL.md | 107 ++++++++ skills/codonfm-setup/agents/openai.yaml | 4 + skills/codonfm-setup/evals/evals.json | 42 ++++ src/tasks.py | 17 +- tests/skills/test_public_skills.py | 277 +++++++++++++++++++++ 15 files changed, 845 insertions(+), 5 deletions(-) create mode 100644 skills/codonfm-embed/SKILL.md create mode 100644 skills/codonfm-embed/agents/openai.yaml create mode 100644 skills/codonfm-embed/evals/evals.json create mode 100644 skills/codonfm-finetune/SKILL.md create mode 100644 skills/codonfm-finetune/agents/openai.yaml create mode 100644 skills/codonfm-finetune/evals/evals.json create mode 100644 skills/codonfm-score/SKILL.md create mode 100644 skills/codonfm-score/agents/openai.yaml create mode 100644 skills/codonfm-score/evals/evals.json create mode 100644 skills/codonfm-setup/SKILL.md create mode 100644 skills/codonfm-setup/agents/openai.yaml create mode 100644 skills/codonfm-setup/evals/evals.json create mode 100644 tests/skills/test_public_skills.py diff --git a/requirements.txt b/requirements.txt index 6ce6974..9c9bcad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,6 @@ +# xFormers CUDA wheels are published on the PyTorch index. +--extra-index-url https://download.pytorch.org/whl/cu124 + # --------- pytorch --------- # torch==2.5.1 torchvision==0.20.1 @@ -22,9 +25,10 @@ pre-commit==4.0.1 # hooks for applying linters on commit rich==13.9.4 # beautiful text formatting in terminal pytest==8.1.1 # tests sh==2.2.2 # for running bash commands in some tests (linux/macos only) +python-dotenv==1.0.1 transformers==4.54.1 polars==1.12.0 -xformers==0.0.28.post3 --index-url https://download.pytorch.org/whl/cu124 +xformers==0.0.28.post3 ninja==1.11.1.1 einops==0.8.0 ipython-autotime==0.3.2 diff --git a/skills/codonfm-embed/SKILL.md b/skills/codonfm-embed/SKILL.md new file mode 100644 index 0000000..4290c22 --- /dev/null +++ b/skills/codonfm-embed/SKILL.md @@ -0,0 +1,69 @@ +--- +name: codonfm-embed +description: Extract frozen CLS embeddings from public CodonFM Encodon checkpoints for coding-sequence property modeling. Use when a user explicitly asks for CodonFM or Encodon embeddings, or wants Encodon features for translation-efficiency, expression, or mRNA-stability modeling. Support Encodon embedding_prediction only; do not claim Decodon embedding support in public CodonFM v1. +--- + +# Extract public Encodon embeddings + +Extract one frozen CLS vector per coding sequence. This workflow writes +embeddings only; it does not automatically train a downstream regressor. + +## Preflight and inputs + +1. Confirm `src/runner.py`, `src/data/codon_bert_dataset.py`, and + `src/inference/encodon.py` exist. +2. Accept only `encodon_80m`, `encodon_600m`, or `encodon_1b`. +3. Require a `.ckpt`, or `.safetensors` with sibling `config.json`. +4. Require CSV columns `id`, `ref_seq`, `value`, and `split`. + +`ref_seq` must be a coding sequence. For extraction-only data, set `value` to +`0.0` and `split` to `test` on every row. Although the public dataset labels +`split` optional, its evaluation path calls the test split and fails without +that column. Normalize sequences to uppercase DNA (`A/C/G/T`) and require +lengths divisible by three. Sequences longer than `--context_length - 2` +codons are truncated rather than embedded in full. + +## Run + +Validate configuration first: + +```bash +python -m src.runner eval \ + --exp_name embed_extract \ + --model_name encodon_1b \ + --checkpoint_path /path/to/encodon_1b.safetensors \ + --data_path /path/to/sequences.csv \ + --process_item codon_sequence \ + --dataset_name CodonBertDataset \ + --task_type embedding_prediction \ + --num_nodes 1 \ + --num_gpus 1 \ + --out_dir /path/to/run \ + --predictions_output_dir /path/to/run/predictions \ + --dryrun +``` + +After the dry run succeeds, rerun without `--dryrun`. + +## Outputs + +- `embeddings_merged.npy`: shape `(number_of_rows, hidden_size)`. +- `ids_merged.npy`: IDs aligned with the embedding rows. + +Use the checked-in Encodon notebooks as downstream-model references: + +- `notebooks/4-EnCodon-Downstream-Task-riboNN.ipynb` +- `notebooks/5-EnCodon-Downstream-Task-mRFP-expression.ipynb` +- `notebooks/6-EnCodon-Downstream-Task-mRNA-stability.ipynb` + +Do not reference `notebooks/te_predictor.py`, `notebooks/mfe_predictor.py`, or +Decodon notebooks because they are absent from public v1. + +## Boundaries + +- Do not use for Decodon; the public repository has no Decodon model or + inference class. +- Do not claim a benchmark-trained regressor generalizes to a new organism, + cell type, or assay without new labeled validation data. +- Do not invoke this skill for a generic expression-prediction request that + does not mention CodonFM or Encodon. diff --git a/skills/codonfm-embed/agents/openai.yaml b/skills/codonfm-embed/agents/openai.yaml new file mode 100644 index 0000000..d3cb6e8 --- /dev/null +++ b/skills/codonfm-embed/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "CodonFM Embeddings" + short_description: "Extract public Encodon sequence embeddings" + default_prompt: "Use $codonfm-embed to extract Encodon embeddings from my coding-sequence CSV." diff --git a/skills/codonfm-embed/evals/evals.json b/skills/codonfm-embed/evals/evals.json new file mode 100644 index 0000000..ee4b14a --- /dev/null +++ b/skills/codonfm-embed/evals/evals.json @@ -0,0 +1,29 @@ +{ + "skill_name": "codonfm-embed", + "evals": [ + { + "id": "codonfm-embed-001", + "prompt": "Extract public Encodon embeddings from sequences.csv on one GPU.", + "expected_output": "The agent requires id/ref_seq/value/split, sets extraction rows to split=test, and uses embedding_prediction with a dry run first.", + "assertions": [ + "The command includes --task_type embedding_prediction", + "The command includes --process_item codon_sequence and --dataset_name CodonBertDataset", + "The command includes --num_gpus 1 and --dryrun", + "The agent requires value and split=test for public-v1 evaluation", + "The agent reports embeddings_merged.npy and ids_merged.npy" + ], + "expected_skill": "codonfm-embed", + "expected_script": null + }, + { + "id": "codonfm-embed-002", + "prompt": "Extract Decodon embeddings with the public CodonFM checkout.", + "expected_output": "The agent explains that public v1 contains no Decodon model or inference implementation.", + "assertions": [ + "The agent does not emit a decodon model command" + ], + "expected_skill": "codonfm-embed", + "expected_script": null + } + ] +} diff --git a/skills/codonfm-finetune/SKILL.md b/skills/codonfm-finetune/SKILL.md new file mode 100644 index 0000000..89aeb20 --- /dev/null +++ b/skills/codonfm-finetune/SKILL.md @@ -0,0 +1,112 @@ +--- +name: codonfm-finetune +description: Fine-tune public CodonFM Encodon checkpoints on labeled coding-sequence or coding-variant data using LoRA, head-only, or full fine-tuning. Use when a user explicitly asks to fine-tune CodonFM or Encodon for regression or classification. Support generic public-v1 Encodon workflows only; reject Decodon, MissenseDataset, missense_synom_agg, and generation workflows. +--- + +# Fine-tune public Encodon + +Use `--pretrained_ckpt_path` for public v1. Do not substitute +`--checkpoint_path`: the public runner does not forward that argument to the +fine-tuning task. + +## Supported strategies + +- `lora`: adapter fine-tuning; default choice for smaller datasets. +- `head_only_random`: freeze the backbone and train a new head. +- `head_only_pretrained`: train an existing compatible pretrained head. +- `full`: update the complete model. + +Accept only `encodon_80m`, `encodon_600m`, or `encodon_1b`. + +## Sequence-level regression or classification + +Require `id`, `ref_seq`, `value`, and `split` columns. `split` values must be +`train`, `val`, or `test`, and every split must be non-empty. Normalize +sequences to uppercase DNA (`A/C/G/T`) and require lengths divisible by three. +Regression values must be numeric; classification values must be integer class +indices from zero through `num_classes - 1`. Use a downstream head for scalar +targets. Ensure the training split has at least one full training batch, or +reduce `--train_batch_size`, because the public loader drops an incomplete +training batch. + +Start with a configuration-only run: + +```bash +python -m src.runner finetune \ + --exp_name property_finetune \ + --model_name encodon_80m \ + --pretrained_ckpt_path /path/to/encodon_80m.safetensors \ + --data_path /path/to/labeled_sequences.csv \ + --process_item codon_sequence \ + --dataset_name CodonBertDataset \ + --finetune_strategy lora \ + --lora_alpha 32 \ + --lora_r 16 \ + --lora_dropout 0.1 \ + --loss_type regression \ + --use_downstream_head \ + --lr 2e-5 \ + --max_steps 1000 \ + --warmup_iterations 100 \ + --check_val_every_n_epoch 1 \ + --train_batch_size 4 \ + --val_batch_size 4 \ + --num_nodes 1 \ + --num_gpus 1 \ + --out_dir /path/to/run \ + --checkpoints_dir /path/to/run/checkpoints \ + --dryrun +``` + +For classification, replace `--loss_type regression` with +`--loss_type classification` and pass the correct `--num_classes`. + +## Generic coding-variant classification + +Use `MutationDataset` only for an ordinary labeled variant head, not the newer +synonymous-codon aggregation loss. Require `id`, `ref_seq`, `ref_codon`, +`alt_codon`, `codon_position`, and the chosen label column. Starting from the +sequence-level command, change/add: + +```text +--process_item mutation_pred_mlm +--dataset_name MutationDataset +--label_col label +--loss_type classification +--num_classes 2 +--use_downstream_head +--extract-seq +--mask_mutation +--train_val_test_ratio 0.8 0.1 0.1 +``` + +Always keep `--mask_mutation` for masked-codon variant inputs. +Keep each variant CSV in a directory without stale `train_idx.npy`, +`val_idx.npy`, or `test_idx.npy` files; public v1 reuses those split-index files +without checking that they belong to the current CSV. + +## Execute and outputs + +After `--dryrun` succeeds, rerun the same command without `--dryrun`. + +Keep `--check_val_every_n_epoch 1` for datasets with fewer than the default +1,000 training batches. Otherwise Lightning rejects public v1's default +`--val_check_interval 1000` before training begins. + +- Checkpoints are written under the explicitly supplied `--checkpoints_dir`, + including `last.ckpt` and configured best checkpoints. +- CSV metrics are written below `--out_dir//version_*` unless W&B is + enabled. +- W&B requires `--enable_wandb`, `--project_name`, and `--entity` together. +- Fine-tuning does not produce prediction arrays; run an evaluation task + separately against the resulting checkpoint. + +## Boundaries + +- Do not use `MissenseDataset`, `missense_seq`, `missense_inference`, + `missense_synom_agg`, or any `--missense_*` flag. They are absent publicly. +- Do not use Decodon model names, CLM preprocessing, organism tokens, or + generation datasets. +- Require an explicit learning rate. Public v1 passes `lr=None` otherwise. +- Treat scientific and clinical validity as a separate validation problem; + successful training does not certify the resulting model. diff --git a/skills/codonfm-finetune/agents/openai.yaml b/skills/codonfm-finetune/agents/openai.yaml new file mode 100644 index 0000000..0dafc77 --- /dev/null +++ b/skills/codonfm-finetune/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "CodonFM Fine-tuning" + short_description: "Fine-tune public Encodon models on labeled data" + default_prompt: "Use $codonfm-finetune to prepare and validate an Encodon fine-tuning run on my labeled data." diff --git a/skills/codonfm-finetune/evals/evals.json b/skills/codonfm-finetune/evals/evals.json new file mode 100644 index 0000000..2902401 --- /dev/null +++ b/skills/codonfm-finetune/evals/evals.json @@ -0,0 +1,40 @@ +{ + "skill_name": "codonfm-finetune", + "evals": [ + { + "id": "codonfm-finetune-001", + "prompt": "Fine-tune public Encodon 80M with LoRA for my continuous sequence label.", + "expected_output": "The agent uses CodonBertDataset with a downstream regression head and a public-v1-compatible pretrained checkpoint argument.", + "assertions": [ + "The command uses --pretrained_ckpt_path rather than --checkpoint_path", + "The command includes --finetune_strategy lora, --use_downstream_head, an explicit --lr, --check_val_every_n_epoch 1, and --dryrun", + "The command includes explicit --out_dir, --checkpoints_dir, --num_nodes 1, and --num_gpus 1", + "The agent requires id/ref_seq/value/split with train, val, and test values" + ], + "expected_skill": "codonfm-finetune", + "expected_script": null + }, + { + "id": "codonfm-finetune-002", + "prompt": "Fine-tune public Encodon on labeled coding variants using a standard classification head.", + "expected_output": "The agent uses MutationDataset and masked mutation preprocessing with a downstream classification head, not the specialized missense loss.", + "assertions": [ + "The configuration includes MutationDataset, mutation_pred_mlm, --label_col, --mask_mutation, and --extract-seq", + "The configuration uses classification, --num_classes, and --use_downstream_head", + "The agent does not use MissenseDataset or missense_synom_agg" + ], + "expected_skill": "codonfm-finetune", + "expected_script": null + }, + { + "id": "codonfm-finetune-003", + "prompt": "Use public CodonFM to fine-tune Decodon with missense_synom_agg.", + "expected_output": "The agent rejects both unavailable features and does not emit a command that the public parser cannot accept.", + "assertions": [ + "The agent identifies Decodon and missense_synom_agg as unavailable in public v1" + ], + "expected_skill": "codonfm-finetune", + "expected_script": null + } + ] +} diff --git a/skills/codonfm-score/SKILL.md b/skills/codonfm-score/SKILL.md new file mode 100644 index 0000000..725dc23 --- /dev/null +++ b/skills/codonfm-score/SKILL.md @@ -0,0 +1,95 @@ +--- +name: codonfm-score +description: Score synonymous or missense coding variants with public CodonFM Encodon checkpoints using masked-codon reference-versus-alternate log-likelihood ratios. Use when a user explicitly asks for CodonFM or Encodon zero-shot variant scoring. Support the public mutation_prediction workflow only; reject Decodon and the newer synonymous-codon-aggregated missense_prediction workflow because they are not present in public CodonFM v1. +--- + +# Score variants with public Encodon + +Run general masked-codon `mutation_prediction` only. This produces a research +signal, not a clinical diagnosis or an expression-direction prediction. + +## Preflight + +1. Confirm `src/runner.py`, `src/data/mutation_dataset.py`, and + `src/inference/encodon.py` exist. +2. Accept only `encodon_80m`, `encodon_600m`, or `encodon_1b` as + `--model_name`. The public parser lists larger names, but its model + configuration does not implement them. +3. Require a `.ckpt` file, or a `.safetensors` file with sibling + `config.json`. +4. Validate the CSV headers before starting a GPU job. + +## Inputs + +Require these CSV columns: + +- `id`: unique row identifier. +- `ref_seq`: reference coding sequence, not genomic DNA with introns, UTR-only + sequence, or protein sequence. +- `ref_codon` and `alt_codon`: three-nucleotide codons. +- `codon_position`: zero-based codon position relative to the CDS. + +With `--extract-seq`, `MutationDataset` extracts an appropriate sequence window +from `ref_seq`; it does not derive or require `alt_seq`. + +Before running, normalize sequences and codons to uppercase DNA (`A/C/G/T`), +require CDS lengths divisible by three, and check every row satisfies: + +```text +0 <= codon_position < len(ref_seq) / 3 +ref_seq[3 * codon_position : 3 * codon_position + 3] == ref_codon +``` + +The public extractor asserts the second condition and otherwise stops the job. + +## Run + +First validate configuration with `--dryrun`: + +```bash +python -m src.runner eval \ + --exp_name variant_scoring \ + --model_name encodon_1b \ + --checkpoint_path /path/to/encodon_1b.safetensors \ + --data_path /path/to/variants.csv \ + --process_item mutation_pred_mlm \ + --dataset_name MutationDataset \ + --task_type mutation_prediction \ + --extract-seq \ + --mask_mutation \ + --num_nodes 1 \ + --num_gpus 1 \ + --out_dir /path/to/run \ + --predictions_output_dir /path/to/run/predictions \ + --dryrun +``` + +Do not remove `--mask_mutation`: without it, the reference codon remains +visible at the scored position and invalidates masked-codon LLR scoring. After +the dry run succeeds, rerun the same command without `--dryrun`. + +## Outputs + +`--predictions_output_dir` receives: + +- `ref_likelihoods_merged.npy` +- `alt_likelihoods_merged.npy` +- `likelihood_ratios_merged.npy` +- `ids_merged.npy` + +Load the arrays with NumPy and align scores by `ids_merged.npy`. The reported +LLR is `log p(ref_codon) - log p(alt_codon)`; a larger positive value means the +alternate codon is less probable in context. It does not say whether +expression goes up or down. + +## Boundaries + +- General `mutation_prediction` handles both synonymous and missense changes. +- Do not use `missense_prediction`, `missense_inference`, `MissenseDataset`, + `mutation_pred_clm`, `--organism_token`, or `--causal`; those are newer + unavailable public-release features. +- If a user asks specifically for synonymous-codon-aggregated missense + scoring, explain that public v1 only provides the general ref/alt LLR. Do not + silently substitute the two methods. +- Do not invoke this skill for a bare “score this variant” request that does + not name CodonFM or Encodon. diff --git a/skills/codonfm-score/agents/openai.yaml b/skills/codonfm-score/agents/openai.yaml new file mode 100644 index 0000000..7dba4a4 --- /dev/null +++ b/skills/codonfm-score/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "CodonFM Variant Scoring" + short_description: "Score coding variants with public Encodon models" + default_prompt: "Use $codonfm-score to validate and score coding variants with a public Encodon checkpoint." diff --git a/skills/codonfm-score/evals/evals.json b/skills/codonfm-score/evals/evals.json new file mode 100644 index 0000000..05c7fda --- /dev/null +++ b/skills/codonfm-score/evals/evals.json @@ -0,0 +1,40 @@ +{ + "skill_name": "codonfm-score", + "evals": [ + { + "id": "codonfm-score-001", + "prompt": "Use public CodonFM Encodon to score my variants.csv on one GPU.", + "expected_output": "The agent uses mutation_prediction with MutationDataset, masks the mutation, extracts CDS context, and reports all four merged NumPy outputs.", + "assertions": [ + "The command includes --task_type mutation_prediction", + "The command includes --process_item mutation_pred_mlm and --dataset_name MutationDataset", + "The command includes --mask_mutation, --extract-seq, and --num_gpus 1", + "The agent runs --dryrun before the real evaluation", + "The agent lists ref_likelihoods_merged.npy, alt_likelihoods_merged.npy, likelihood_ratios_merged.npy, and ids_merged.npy" + ], + "expected_skill": "codonfm-score", + "expected_script": null + }, + { + "id": "codonfm-score-002", + "prompt": "Run CodonFM synonymous-codon-aggregated missense_prediction on the public repository.", + "expected_output": "The agent explains that public v1 supports only the general ref/alt LLR and does not silently replace the requested aggregation method.", + "assertions": [ + "The agent does not run missense_prediction or missense_inference", + "The agent distinguishes general mutation_prediction from synonymous-codon aggregation" + ], + "expected_skill": "codonfm-score", + "expected_script": null + }, + { + "id": "codonfm-score-003", + "prompt": "Score this variant.", + "expected_output": "The agent does not assume CodonFM because no model or tool was named.", + "assertions": [ + "The agent does not invoke a codonfm-* skill without CodonFM or Encodon context" + ], + "expected_skill": null, + "expected_script": null + } + ] +} diff --git a/skills/codonfm-setup/SKILL.md b/skills/codonfm-setup/SKILL.md new file mode 100644 index 0000000..788102e --- /dev/null +++ b/skills/codonfm-setup/SKILL.md @@ -0,0 +1,107 @@ +--- +name: codonfm-setup +description: Set up the public CodonFM v1 repository and download public Encodon checkpoints. Use for requests to build or launch the CodonFM development container, configure local data/checkpoint mounts, verify GPU access, or download public Encodon 80M, 600M, 1B, or Cdwt-1B weights. Do not use for Decodon, Encodon 5B/10B, missense-aggregation, or codon-optimization setup because those implementations are not in the public repository. +--- + +# CodonFM public setup + +Operate from the public CodonFM repository root. Support only the checked-in +public v1 code and public Encodon checkpoints. + +## Preflight + +1. Confirm `Dockerfile`, `run_dev.sh`, and `src/runner.py` exist. +2. Confirm `docker info` succeeds and `nvidia-smi` sees the intended GPU. +3. Run `bash -n run_dev.sh` before launching it. +4. Resolve explicit host paths for data and checkpoints. Do not rely on the + `/data/codonfm` defaults unless the user confirms they exist. +5. Check for an existing container before launch: + +```bash +docker ps -a --filter name='^/codon-fm-dev-container$' +``` + +If an exact-name container is running, `run_dev.sh` stops and removes it; tell +the user before replacement. If it is stopped, the script cannot reuse the +name, so obtain confirmation before removing it with +`docker rm codon-fm-dev-container`. The public script also uses host +networking/IPC and mounts the user's SSH directory read-only; disclose this +before execution. + +## Build and launch + +```bash +cd /path/to/CodonFM +bash run_dev.sh \ + --data-dir /absolute/path/to/data \ + --checkpoints-dir /absolute/path/to/checkpoints +``` + +The host checkpoint directory is mounted at `/data/checkpoints` inside the +container. The image is `codon-fm-dev`; the container is +`codon-fm-dev-container`. + +Use only the checked-in public code and the dependency versions declared in +its `Dockerfile` and `requirements.txt`. + +## Run directly without Docker + +Use this path when Docker is unavailable and the host has a compatible NVIDIA +driver. The tested baseline is Python 3.11, CUDA-capable PyTorch, and one GPU. +Operate from a writable checkout and use a dedicated virtual environment: + +```bash +cd /path/to/CodonFM +python3.11 -m venv .venv +. .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install -r requirements.txt +mkdir -p /absolute/path/to/codonfm-matplotlib-cache +export MPLCONFIGDIR=/absolute/path/to/codonfm-matplotlib-cache +python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))" +``` + +Expect `True` and the selected GPU name. The requirements file configures the +CUDA 12.4 PyTorch index for xFormers. Use explicit host paths in all subsequent +runner commands; unlike the container path, no `/data/checkpoints` mount is +created. + +## Download a checkpoint + +Run inside the container, or in another environment with Hugging Face Hub: + +```bash +hf download nvidia/NV-CodonFM-Encodon-1B-v1 \ + --local-dir /data/checkpoints/encodon-1b +``` + +Other supported public model IDs are: + +- `nvidia/NV-CodonFM-Encodon-80M-v1` +- `nvidia/NV-CodonFM-Encodon-600M-v1` +- `nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1` + +Use `--model_name encodon_80m`, `encodon_600m`, or `encodon_1b` according to +architecture size. Cdwt-1B uses `encodon_1b` because Cdwt is a checkpoint +training property, not a separate architecture. + +For `.safetensors`, keep `config.json` in the same directory as the model +file. Never invent a Decodon or undocumented checkpoint path. + +## Verify + +```bash +docker exec codon-fm-dev-container python -c \ + "import torch; print(torch.cuda.is_available())" +``` + +Expect `True` on a configured NVIDIA GPU host. If Docker or a GPU is +unavailable, report the missing prerequisite; do not claim setup succeeded. + +## Public-v1 boundaries + +- Supported: Encodon 80M, 600M, 1B, and Cdwt-1B. +- Not supported: Decodon, Encodon 5B/10B, sequence generation, specialized + missense aggregation/fine-tuning, and `scripts/codon_optimize.py`. +- CodonFM consumes coding sequences. It is not a variant caller, aligner, GTF + annotator, or general VCF analysis tool. diff --git a/skills/codonfm-setup/agents/openai.yaml b/skills/codonfm-setup/agents/openai.yaml new file mode 100644 index 0000000..4c7925b --- /dev/null +++ b/skills/codonfm-setup/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "CodonFM Setup" + short_description: "Set up public CodonFM and Encodon checkpoints" + default_prompt: "Use $codonfm-setup to configure the public CodonFM environment and download an Encodon checkpoint." diff --git a/skills/codonfm-setup/evals/evals.json b/skills/codonfm-setup/evals/evals.json new file mode 100644 index 0000000..e42fe9b --- /dev/null +++ b/skills/codonfm-setup/evals/evals.json @@ -0,0 +1,42 @@ +{ + "skill_name": "codonfm-setup", + "evals": [ + { + "id": "codonfm-setup-001", + "prompt": "Set up the public CodonFM repository and download Encodon 1B.", + "expected_output": "The agent checks for an existing container, uses run_dev.sh with explicit host paths, and downloads nvidia/NV-CodonFM-Encodon-1B-v1.", + "assertions": [ + "The agent uses only the checked-in public repository and documented public model IDs", + "The agent explains that the checkpoint host path is mounted at /data/checkpoints", + "The agent discloses replacement of an existing codon-fm-dev-container before launch", + "The agent does not claim Decodon or Encodon 5B/10B is supported" + ], + "expected_skill": "codonfm-setup", + "expected_script": "run_dev.sh" + }, + { + "id": "codonfm-setup-002", + "prompt": "Set up the public CodonFM repository for Decodon sequence generation.", + "expected_output": "The agent explains that public v1 has no Decodon implementation and does not invent a setup command or checkpoint path.", + "assertions": [ + "The agent does not attempt a Decodon download", + "The agent identifies Decodon as unavailable in this public checkout" + ], + "expected_skill": "codonfm-setup", + "expected_script": null + }, + { + "id": "codonfm-setup-003", + "prompt": "Docker is unavailable. Set up public CodonFM directly on my CUDA host.", + "expected_output": "The agent creates a Python 3.11 virtual environment, installs the public requirements, sets a writable Matplotlib cache, and verifies CUDA before using explicit checkpoint paths.", + "assertions": [ + "The instructions use python3.11 -m venv and python -m pip install -r requirements.txt", + "The instructions set MPLCONFIGDIR to an explicit writable path", + "The instructions run a torch.cuda.is_available verification", + "The agent does not require Docker for the direct-host path" + ], + "expected_skill": "codonfm-setup", + "expected_script": null + } + ] +} diff --git a/src/tasks.py b/src/tasks.py index 0a8d48c..57c403f 100644 --- a/src/tasks.py +++ b/src/tasks.py @@ -157,10 +157,19 @@ def evaluate( model.configure_model() data.setup("test") - if os.path.exists(model_ckpt_path): + # Safetensors files contain model weights only. Loading one again through + # torch.load() raises an unpickling error, so only inspect Lightning + # checkpoints for an optional datamodule state. + if ( + os.path.exists(model_ckpt_path) + and Path(model_ckpt_path).suffix.lower() == ".ckpt" + ): logging.info(f"Loading dataset checkpoint from {model_ckpt_path}") - data.load_state_dict(torch.load(model_ckpt_path)) - model.prediction_counter = data.init_global_step + checkpoint = torch.load(model_ckpt_path, map_location="cpu") + datamodule_state = checkpoint.get(data.__class__.__qualname__) + if datamodule_state is not None: + data.load_state_dict(datamodule_state) + model.prediction_counter = data.init_global_step trainer.logger = logger trainer.callbacks = list(callbacks.values()) @@ -169,4 +178,4 @@ def evaluate( trainer.predict(model, datamodule=data, return_predictions=False) - return \ No newline at end of file + return diff --git a/tests/skills/test_public_skills.py b/tests/skills/test_public_skills.py new file mode 100644 index 0000000..30c5487 --- /dev/null +++ b/tests/skills/test_public_skills.py @@ -0,0 +1,277 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Static contract tests for the public CodonFM skills. + +These tests intentionally use only the Python standard library so command +usage can be checked before installing the GPU runtime. +""" + +import argparse +import ast +import json +import re +import shlex +import tempfile +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +SKILLS_ROOT = REPO_ROOT / "skills" +PUBLIC_SKILLS = { + "codonfm-setup", + "codonfm-score", + "codonfm-embed", + "codonfm-finetune", +} + + +def _frontmatter(skill_text: str) -> dict[str, str]: + match = re.match(r"\A---\n(.*?)\n---\n", skill_text, re.DOTALL) + if match is None: + raise AssertionError("SKILL.md is missing YAML frontmatter") + result = {} + for line in match.group(1).splitlines(): + key, separator, value = line.partition(":") + if not separator: + raise AssertionError(f"Invalid frontmatter line: {line}") + result[key.strip()] = value.strip() + return result + + +def _runner_commands(skill_text: str) -> list[list[str]]: + commands = [] + for block in re.findall(r"```bash\n(.*?)```", skill_text, re.DOTALL): + normalized = block.replace("\\\n", " ") + tokens = shlex.split(normalized, comments=True) + for index in range(len(tokens) - 3): + if tokens[index:index + 3] == ["python", "-m", "src.runner"]: + commands.append(tokens[index + 3:]) + break + return commands + + +def _public_runner_parser() -> argparse.ArgumentParser: + """Build the checked-in parser without importing the runner's GPU deps.""" + tree = ast.parse((REPO_ROOT / "src/runner.py").read_text()) + get_parser = next( + node + for node in tree.body + if isinstance(node, ast.FunctionDef) and node.name == "get_parser" + ) + parser_module = ast.Module(body=[get_parser], type_ignores=[]) + namespace = {"argparse": argparse} + exec(compile(parser_module, "src/runner.py", "exec"), namespace) + return namespace["get_parser"]() + + +def _evaluate_function(namespace): + """Load only tasks.evaluate so it can be tested without GPU packages.""" + tree = ast.parse((REPO_ROOT / "src/tasks.py").read_text()) + evaluate = next( + node + for node in tree.body + if isinstance(node, ast.FunctionDef) and node.name == "evaluate" + ) + module = ast.Module(body=[evaluate], type_ignores=[]) + exec(compile(module, "src/tasks.py", "exec"), namespace) + return namespace["evaluate"] + + +class PublicSkillContractTests(unittest.TestCase): + def test_expected_public_skill_set(self): + actual = { + path.name + for path in SKILLS_ROOT.iterdir() + if path.is_dir() and (path / "SKILL.md").exists() + } + self.assertTrue(PUBLIC_SKILLS.issubset(actual)) + self.assertFalse((SKILLS_ROOT / "codonfm-optimize").exists()) + + def test_frontmatter_and_ui_metadata(self): + for skill_name in PUBLIC_SKILLS: + with self.subTest(skill=skill_name): + skill_dir = SKILLS_ROOT / skill_name + text = (skill_dir / "SKILL.md").read_text() + metadata = _frontmatter(text) + self.assertEqual(set(metadata), {"name", "description"}) + self.assertEqual(metadata["name"], skill_name) + self.assertNotIn("TODO", text) + + ui = (skill_dir / "agents/openai.yaml").read_text() + self.assertIn("display_name:", ui) + self.assertIn("short_description:", ui) + self.assertIn(f"${skill_name}", ui) + + def test_evals_are_valid_and_named(self): + for skill_name in PUBLIC_SKILLS: + with self.subTest(skill=skill_name): + eval_path = SKILLS_ROOT / skill_name / "evals/evals.json" + payload = json.loads(eval_path.read_text()) + self.assertEqual(payload["skill_name"], skill_name) + self.assertGreater(len(payload["evals"]), 0) + ids = [case["id"] for case in payload["evals"]] + self.assertEqual(len(ids), len(set(ids))) + + def test_documented_runner_commands_parse(self): + parser = _public_runner_parser() + commands = [] + for skill_name in PUBLIC_SKILLS: + text = (SKILLS_ROOT / skill_name / "SKILL.md").read_text() + commands.extend((skill_name, command) for command in _runner_commands(text)) + + self.assertEqual({name for name, _ in commands}, { + "codonfm-score", + "codonfm-embed", + "codonfm-finetune", + }) + for skill_name, command in commands: + with self.subTest(skill=skill_name): + parser.parse_args(command) + + def test_fragile_command_requirements(self): + score = _runner_commands( + (SKILLS_ROOT / "codonfm-score/SKILL.md").read_text() + )[0] + self.assertIn("--mask_mutation", score) + self.assertIn("--extract-seq", score) + self.assertIn("--dryrun", score) + self.assertEqual(score[score.index("--num_gpus") + 1], "1") + + embed = _runner_commands( + (SKILLS_ROOT / "codonfm-embed/SKILL.md").read_text() + )[0] + self.assertIn("--dryrun", embed) + self.assertEqual(embed[embed.index("--num_gpus") + 1], "1") + + finetune = _runner_commands( + (SKILLS_ROOT / "codonfm-finetune/SKILL.md").read_text() + )[0] + self.assertIn("--pretrained_ckpt_path", finetune) + self.assertNotIn("--checkpoint_path", finetune) + for flag in ( + "--lr", + "--check_val_every_n_epoch", + "--checkpoints_dir", + "--use_downstream_head", + "--dryrun", + ): + self.assertIn(flag, finetune) + self.assertEqual( + finetune[finetune.index("--check_val_every_n_epoch") + 1], "1" + ) + + def test_no_unavailable_feature_in_runner_commands(self): + forbidden = { + "MissenseDataset", + "missense_prediction", + "missense_synom_agg", + "missense_inference", + "missense_seq", + "decodon_200m", + "decodon_1b", + "mutation_pred_clm", + } + for skill_name in PUBLIC_SKILLS: + text = (SKILLS_ROOT / skill_name / "SKILL.md").read_text() + for command in _runner_commands(text): + with self.subTest(skill=skill_name, command=command): + self.assertTrue(forbidden.isdisjoint(command)) + + def test_setup_usage_matches_public_script(self): + text = (SKILLS_ROOT / "codonfm-setup/SKILL.md").read_text() + self.assertIn("bash run_dev.sh", text) + self.assertIn("--data-dir", text) + self.assertIn("--checkpoints-dir", text) + self.assertIn("/data/checkpoints", text) + self.assertIn("hf download nvidia/NV-CodonFM-Encodon-1B-v1", text) + self.assertIn("python3.11 -m venv .venv", text) + self.assertIn("python -m pip install -r requirements.txt", text) + self.assertIn("export MPLCONFIGDIR=", text) + self.assertIn("Use only the checked-in public code", text) + + def test_safetensors_eval_is_not_loaded_as_a_lightning_checkpoint(self): + calls = {"torch_load": 0, "predict": 0} + + class FakeTorch: + @staticmethod + def load(*args, **kwargs): + calls["torch_load"] += 1 + raise AssertionError("torch.load must not read safetensors") + + class FakeLogger: + def log_hyperparams(self, config): + self.config = config + + class FakeData: + init_global_step = 0 + + def setup(self, stage): + self.stage = stage + + def load_state_dict(self, state): + self.state = state + + class FakeModel: + prediction_counter = 0 + + def configure_model(self): + self.configured = True + + class FakeTrainer: + def __init__(self, **kwargs): + self.kwargs = kwargs + + def predict(self, *args, **kwargs): + calls["predict"] += 1 + + namespace = { + "Any": object, + "Dict": dict, + "Path": Path, + "Trainer": FakeTrainer, + "logging": type("Logging", (), {"info": staticmethod(lambda message: None)}), + "os": __import__("os"), + "seed_everything": lambda *args, **kwargs: None, + "torch": FakeTorch, + } + evaluate = _evaluate_function(namespace) + + with tempfile.TemporaryDirectory() as temp_dir: + model_path = Path(temp_dir) / "model.safetensors" + model_path.touch() + data = FakeData() + model = FakeModel() + evaluate( + config={ + "log": FakeLogger(), + "data": data, + "trainer": {}, + "model": model, + "callbacks": {}, + }, + config_dict={}, + model_ckpt_path=str(model_path), + out_dir=temp_dir, + ) + + self.assertEqual(calls["torch_load"], 0) + self.assertEqual(calls["predict"], 1) + self.assertEqual(data.stage, "test") + self.assertTrue(model.configured) + + def test_checked_in_references_exist(self): + for relative_path in ( + "notebooks/4-EnCodon-Downstream-Task-riboNN.ipynb", + "notebooks/5-EnCodon-Downstream-Task-mRFP-expression.ipynb", + "notebooks/6-EnCodon-Downstream-Task-mRNA-stability.ipynb", + "src/data/codon_bert_dataset.py", + "src/data/mutation_dataset.py", + "src/inference/encodon.py", + ): + self.assertTrue((REPO_ROOT / relative_path).is_file(), relative_path) + + +if __name__ == "__main__": + unittest.main() From 87006b1e98828c0fed0925a29375d4e73e7b9245 Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Thu, 3 Sep 2026 16:01:33 -0700 Subject: [PATCH 02/13] Add author metadata Signed-off-by: Ohad Mosafi --- skills/codonfm-embed/SKILL.md | 2 ++ skills/codonfm-finetune/SKILL.md | 2 ++ skills/codonfm-score/SKILL.md | 2 ++ skills/codonfm-setup/SKILL.md | 2 ++ 4 files changed, 8 insertions(+) diff --git a/skills/codonfm-embed/SKILL.md b/skills/codonfm-embed/SKILL.md index 4290c22..32a5be1 100644 --- a/skills/codonfm-embed/SKILL.md +++ b/skills/codonfm-embed/SKILL.md @@ -1,6 +1,8 @@ --- name: codonfm-embed description: Extract frozen CLS embeddings from public CodonFM Encodon checkpoints for coding-sequence property modeling. Use when a user explicitly asks for CodonFM or Encodon embeddings, or wants Encodon features for translation-efficiency, expression, or mRNA-stability modeling. Support Encodon embedding_prediction only; do not claim Decodon embedding support in public CodonFM v1. +metadata: + author: "NVIDIA BioNeMo " --- # Extract public Encodon embeddings diff --git a/skills/codonfm-finetune/SKILL.md b/skills/codonfm-finetune/SKILL.md index 89aeb20..d32e6ef 100644 --- a/skills/codonfm-finetune/SKILL.md +++ b/skills/codonfm-finetune/SKILL.md @@ -1,6 +1,8 @@ --- name: codonfm-finetune description: Fine-tune public CodonFM Encodon checkpoints on labeled coding-sequence or coding-variant data using LoRA, head-only, or full fine-tuning. Use when a user explicitly asks to fine-tune CodonFM or Encodon for regression or classification. Support generic public-v1 Encodon workflows only; reject Decodon, MissenseDataset, missense_synom_agg, and generation workflows. +metadata: + author: "NVIDIA BioNeMo " --- # Fine-tune public Encodon diff --git a/skills/codonfm-score/SKILL.md b/skills/codonfm-score/SKILL.md index 725dc23..5f55a8a 100644 --- a/skills/codonfm-score/SKILL.md +++ b/skills/codonfm-score/SKILL.md @@ -1,6 +1,8 @@ --- name: codonfm-score description: Score synonymous or missense coding variants with public CodonFM Encodon checkpoints using masked-codon reference-versus-alternate log-likelihood ratios. Use when a user explicitly asks for CodonFM or Encodon zero-shot variant scoring. Support the public mutation_prediction workflow only; reject Decodon and the newer synonymous-codon-aggregated missense_prediction workflow because they are not present in public CodonFM v1. +metadata: + author: "NVIDIA BioNeMo " --- # Score variants with public Encodon diff --git a/skills/codonfm-setup/SKILL.md b/skills/codonfm-setup/SKILL.md index 788102e..b0c1749 100644 --- a/skills/codonfm-setup/SKILL.md +++ b/skills/codonfm-setup/SKILL.md @@ -1,6 +1,8 @@ --- name: codonfm-setup description: Set up the public CodonFM v1 repository and download public Encodon checkpoints. Use for requests to build or launch the CodonFM development container, configure local data/checkpoint mounts, verify GPU access, or download public Encodon 80M, 600M, 1B, or Cdwt-1B weights. Do not use for Decodon, Encodon 5B/10B, missense-aggregation, or codon-optimization setup because those implementations are not in the public repository. +metadata: + author: "NVIDIA BioNeMo " --- # CodonFM public setup From db15d24f2e23ae732a247321132f179c7001d10d Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Thu, 3 Sep 2026 16:07:33 -0700 Subject: [PATCH 03/13] nvskills workflow file Signed-off-by: Ohad Mosafi --- .github/workflows/request-nvskills-ci.yml | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/request-nvskills-ci.yml diff --git a/.github/workflows/request-nvskills-ci.yml b/.github/workflows/request-nvskills-ci.yml new file mode 100644 index 0000000..a88a057 --- /dev/null +++ b/.github/workflows/request-nvskills-ci.yml @@ -0,0 +1,26 @@ +name: Request NVSkills CI + +on: + issue_comment: + types: [created] + pull_request: + types: [opened, reopened, synchronize, ready_for_review] + push: + +jobs: + request: + if: > + github.event_name == 'pull_request' || + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + startsWith(github.event.comment.body, '/nvskills-ci')) || + (github.event_name == 'push' && + github.actor == (vars.NVSKILLS_SIGNATURE_PUSH_ACTOR || 'nv-skills-ci[bot]') && + startsWith(github.event.head_commit.message, vars.NVSKILLS_SIGNATURE_COMMIT_TITLE || 'Attach NVSkills validation signatures')) + permissions: + contents: read + pull-requests: read + statuses: read + uses: NVIDIA/skills/.github/workflows/team-request.yml@main + secrets: + NVSKILLS_CI_DISPATCH_TOKEN: ${{ secrets.NVSKILLS_CI_DISPATCH_TOKEN }} \ No newline at end of file From 7dc0a0fad34dc5115f05ae8b2740b669ab3eb210 Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Wed, 9 Sep 2026 15:19:03 -0700 Subject: [PATCH 04/13] condonfm skills data Signed-off-by: Ohad Mosafi --- skills/codonfm-embed/SKILL.md | 50 ++++-- skills/codonfm-embed/evals/evals.json | 29 ++-- .../evals/files/codonfm_source.zip | Bin 0 -> 81758 bytes .../evals/files/encodon_checkpoint.json | 33 ++++ .../codonfm-embed/evals/files/sequences.csv | 3 + skills/codonfm-finetune/SKILL.md | 142 ++++++++++++++---- skills/codonfm-finetune/evals/evals.json | 50 ++++-- .../evals/files/codonfm_source.zip | Bin 0 -> 81758 bytes .../evals/files/encodon_checkpoint.json | 33 ++++ .../evals/files/ribonn_smoke.provenance.json | 12 ++ .../evals/files/ribonn_smoke.tsv | 13 ++ .../evals/files/variants_labeled.csv | 21 +++ .../files/variants_labeled.provenance.json | 6 + .../scripts/prepare_ribonn.py | 119 +++++++++++++++ skills/codonfm-score/SKILL.md | 59 ++++++-- skills/codonfm-score/evals/evals.json | 37 +++-- .../evals/files/codonfm_source.zip | Bin 0 -> 81758 bytes .../evals/files/encodon_checkpoint.json | 33 ++++ skills/codonfm-score/evals/files/variants.csv | 3 + skills/codonfm-setup/SKILL.md | 62 ++++++-- skills/codonfm-setup/evals/evals.json | 44 ++++-- .../evals/files/codonfm_source.zip | Bin 0 -> 81758 bytes .../evals/files/encodon_checkpoint.json | 33 ++++ skills/stage_eval_context.py | 49 ++++++ tests/skills/test_public_skills.py | 61 +++++++- 25 files changed, 775 insertions(+), 117 deletions(-) create mode 100644 skills/codonfm-embed/evals/files/codonfm_source.zip create mode 100644 skills/codonfm-embed/evals/files/encodon_checkpoint.json create mode 100644 skills/codonfm-embed/evals/files/sequences.csv create mode 100644 skills/codonfm-finetune/evals/files/codonfm_source.zip create mode 100644 skills/codonfm-finetune/evals/files/encodon_checkpoint.json create mode 100644 skills/codonfm-finetune/evals/files/ribonn_smoke.provenance.json create mode 100644 skills/codonfm-finetune/evals/files/ribonn_smoke.tsv create mode 100644 skills/codonfm-finetune/evals/files/variants_labeled.csv create mode 100644 skills/codonfm-finetune/evals/files/variants_labeled.provenance.json create mode 100644 skills/codonfm-finetune/scripts/prepare_ribonn.py create mode 100644 skills/codonfm-score/evals/files/codonfm_source.zip create mode 100644 skills/codonfm-score/evals/files/encodon_checkpoint.json create mode 100644 skills/codonfm-score/evals/files/variants.csv create mode 100644 skills/codonfm-setup/evals/files/codonfm_source.zip create mode 100644 skills/codonfm-setup/evals/files/encodon_checkpoint.json create mode 100644 skills/stage_eval_context.py diff --git a/skills/codonfm-embed/SKILL.md b/skills/codonfm-embed/SKILL.md index 32a5be1..2e72c40 100644 --- a/skills/codonfm-embed/SKILL.md +++ b/skills/codonfm-embed/SKILL.md @@ -10,12 +10,32 @@ metadata: Extract one frozen CLS vector per coding sequence. This workflow writes embeddings only; it does not automatically train a downstream regressor. +## Instructions + +Resolve the sequence CSV, checkpoint, and output directory from the request and +available files. Validate inputs before extraction. Execution requires the +project's ML dependencies and a compatible NVIDIA GPU. If a required resource +is unavailable, complete the available preparation and return the command with +that prerequisite identified. When extraction is requested and resources are +ready, execute and verify the embedding arrays. A request for preparation ends +with the inputs and command. If no sequences were supplied, report the required +inputs. For Decodon, inspect the [public parser](../../src/runner.py) and +[model configuration](../../src/config.py), explain the missing implementation, +and finish without attempting installation or model development. + +For a demonstration use `nvidia/NV-CodonFM-Encodon-80M-v1`, revision +`399ca9fe17b57941a7bebc6788033919b417413c`, file +`NV-CodonFM-Encodon-80M-v1.safetensors` with sibling `config.json`. +Reuse an existing checkpoint or download it when needed for the requested work. +Preserve a user's explicit checkpoint choice. + ## Preflight and inputs 1. Confirm `src/runner.py`, `src/data/codon_bert_dataset.py`, and `src/inference/encodon.py` exist. 2. Accept only `encodon_80m`, `encodon_600m`, or `encodon_1b`. -3. Require a `.ckpt`, or `.safetensors` with sibling `config.json`. +3. For execution require a `.ckpt`, or `.safetensors` with sibling `config.json`; + input preparation can use a planned path. 4. Require CSV columns `id`, `ref_seq`, `value`, and `split`. `ref_seq` must be a coding sequence. For extraction-only data, set `value` to @@ -25,27 +45,37 @@ that column. Normalize sequences to uppercase DNA (`A/C/G/T`) and require lengths divisible by three. Sequences longer than `--context_length - 2` codons are truncated rather than embedded in full. -## Run +## Examples -Validate configuration first: +Set `CODONFM_DATA_PATH` to the sequence CSV, `CODONFM_CHECKPOINT_PATH` to the +checkpoint, and `CODONFM_RUN_DIR` to your chosen output directory: ```bash python -m src.runner eval \ --exp_name embed_extract \ - --model_name encodon_1b \ - --checkpoint_path /path/to/encodon_1b.safetensors \ - --data_path /path/to/sequences.csv \ + --model_name encodon_80m \ + --checkpoint_path "$CODONFM_CHECKPOINT_PATH" \ + --data_path "$CODONFM_DATA_PATH" \ --process_item codon_sequence \ --dataset_name CodonBertDataset \ --task_type embedding_prediction \ --num_nodes 1 \ --num_gpus 1 \ - --out_dir /path/to/run \ - --predictions_output_dir /path/to/run/predictions \ - --dryrun + --num_workers 0 \ + --val_batch_size 2 \ + --out_dir "$CODONFM_RUN_DIR" \ + --predictions_output_dir "$CODONFM_RUN_DIR/predictions" ``` -After the dry run succeeds, rerun without `--dryrun`. +For preparation requests, inspect the CSV directly against the input schema +above and report the test-row count and sequence checks. Extra columns are +allowed; extraction does not require a measured target. This does not require +the ML runtime. The command above performs extraction when resources are ready. + +The existing `--dryrun` optionally builds runtime configuration and skips +execution. It requires the ML dependencies, can create the prediction directory, +and does not read the CSV or load weights. Do not use it as evidence that inputs, +checkpoint compatibility, or embedding quality have been validated. ## Outputs diff --git a/skills/codonfm-embed/evals/evals.json b/skills/codonfm-embed/evals/evals.json index ee4b14a..e155005 100644 --- a/skills/codonfm-embed/evals/evals.json +++ b/skills/codonfm-embed/evals/evals.json @@ -3,24 +3,33 @@ "evals": [ { "id": "codonfm-embed-001", - "prompt": "Extract public Encodon embeddings from sequences.csv on one GPU.", - "expected_output": "The agent requires id/ref_seq/value/split, sets extraction rows to split=test, and uses embedding_prediction with a dry run first.", + "prompt": "Validate the supplied sequences.csv and prepare a public Encodon embedding-extraction command. Explain which rows will be processed and how to associate the output embeddings with sequence IDs. Use the supplied public source and checkpoint metadata.", + "files": [ + "files/codonfm_source.zip", + "files/encodon_checkpoint.json", + "files/sequences.csv" + ], + "expected_output": "Two validated test rows and a public embedding_prediction command with the correct embedding/ID output contract.", "assertions": [ - "The command includes --task_type embedding_prediction", - "The command includes --process_item codon_sequence and --dataset_name CodonBertDataset", - "The command includes --num_gpus 1 and --dryrun", - "The agent requires value and split=test for public-v1 evaluation", - "The agent reports embeddings_merged.npy and ids_merged.npy" + "The command uses embedding_prediction, codon_sequence, and CodonBertDataset", + "The command specifies --checkpoint_path and output/prediction paths appropriate to the chosen working directory", + "The agent validates both sequence rows, including value and split=test, and explains that evaluation processes the test split", + "The response identifies embeddings_merged.npy and ids_merged.npy and explains their row alignment without fabricating embeddings" ], "expected_skill": "codonfm-embed", "expected_script": null }, { "id": "codonfm-embed-002", - "prompt": "Extract Decodon embeddings with the public CodonFM checkout.", - "expected_output": "The agent explains that public v1 contains no Decodon model or inference implementation.", + "prompt": "Does the public CodonFM implementation support extracting Decodon embeddings? Check the supplied source and explain the limitation, if any.", + "files": [ + "files/codonfm_source.zip", + "files/encodon_checkpoint.json" + ], + "expected_output": "The agent identifies the absence of a public Decodon model and inference implementation.", "assertions": [ - "The agent does not emit a decodon model command" + "The agent explains that the supplied public source has no Decodon model/inference implementation and cites inspected files", + "The agent does not invent a Decodon command or attempt to implement the missing model" ], "expected_skill": "codonfm-embed", "expected_script": null diff --git a/skills/codonfm-embed/evals/files/codonfm_source.zip b/skills/codonfm-embed/evals/files/codonfm_source.zip new file mode 100644 index 0000000000000000000000000000000000000000..eb76611c865b4d3638307c06bf685f7a19c59f46 GIT binary patch literal 81758 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 V4ww-X6kgzq61c deadline: + raise TimeoutError("Dataset preparation exceeded the network time budget; use --input for an offline file") + try: + start, length, fold = int(raw["utr5_size"]), int(raw["cds_size"]), int(raw["fold"]) + value = float(raw["mean_te"]) + transcript = raw["tx_sequence"].upper().replace("U", "T") + row_id = raw["transcript_id"].strip() + except (TypeError, ValueError, AttributeError): + skipped["invalid_metadata"] += 1 + continue + if not row_id or start < 0 or length <= 0 or start + length > len(transcript) or length % 3: + skipped["invalid_cds_bounds_or_id"] += 1 + continue + sequence = transcript[start:start + length] + if set(sequence) - set("ACGT") or not math.isfinite(value) or fold not in range(10): + skipped["invalid_sequence_label_or_fold"] += 1 + continue + if length // 3 > max_codons: + skipped["cds_exceeds_context"] += 1 + continue + split = "val" if fold == 8 else "test" if fold == 9 else "train" + if row_id in seen_ids: + skipped["duplicate_transcript"] += 1 + continue + if sequence in sequence_splits and sequence_splits[sequence] != split: + raise ValueError("Identical CDS appears in different source folds; resolve split leakage before training") + seen_ids.add(row_id) + sequence_splits[sequence] = split + if max_rows_per_split and counts[split] >= max_rows_per_split: + continue + rows.append({"id": row_id, "ref_seq": sequence, "value": value, "split": split}) + counts[split] += 1 + if max_rows_per_split and all(counts[s] >= max_rows_per_split for s in ("train", "val", "test")): + break + if not all(counts[s] for s in ("train", "val", "test")): + raise ValueError("Prepared data must have non-empty train, val, and test splits; check source folds 0-9") + return rows, { + "rows_examined": examined, "rows_written": len(rows), "split_counts": dict(counts), + "skipped": dict(skipped), "label": "mean_te (unchanged)", + "fold_mapping": {"train": list(range(8)), "val": [8], "test": [9]}, + "max_rows_per_split": max_rows_per_split, "max_codons": max_codons, + "purpose": "input preparation; a capped subset is not a scientific benchmark", + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--input", type=Path, help="Existing upstream-format TSV; omit to stream the pinned public data") + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--max-rows-per-split", type=int, default=8, help="Default 8 for a small example; 0 processes all rows") + parser.add_argument("--max-codons", type=int, default=2046) + args = parser.parse_args() + if args.input and args.input.resolve() == args.output.resolve(): + parser.error("Input and output must be different files") + try: + if args.input: + with args.input.open(encoding="utf-8-sig", newline="") as handle: + rows, report = prepare(handle, args.max_rows_per_split, args.max_codons) + else: + # No retry loop or full-dataset download for the default example. + deadline = time.monotonic() + 60 + with urllib.request.urlopen(DATA_URL, timeout=10) as response: + with io.TextIOWrapper(response, encoding="utf-8-sig", newline="") as handle: + rows, report = prepare(handle, args.max_rows_per_split, args.max_codons, deadline) + report.update({"source": str(args.input) if args.input else DATA_URL, "upstream_url": DATA_URL}) + args.output.parent.mkdir(parents=True, exist_ok=True) + with args.output.open("w", newline="", encoding="utf-8") as handle: + writer = csv.DictWriter(handle, fieldnames=["id", "ref_seq", "value", "split"]) + writer.writeheader() + writer.writerows(rows) + args.output.with_suffix(".metadata.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") + except (ValueError, OSError, urllib.error.URLError) as exc: + parser.exit(1, f"Preparation failed: {exc}\n") + print(json.dumps(report, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/skills/codonfm-score/SKILL.md b/skills/codonfm-score/SKILL.md index 5f55a8a..2bd51f6 100644 --- a/skills/codonfm-score/SKILL.md +++ b/skills/codonfm-score/SKILL.md @@ -10,6 +10,30 @@ metadata: Run general masked-codon `mutation_prediction` only. This produces a research signal, not a clinical diagnosis or an expression-direction prediction. +## Instructions + +Check whether the request is executable in public v1 before installing or +downloading anything. For synonymous-codon aggregation or Decodon, inspect the +[parser](../../src/runner.py) and [model configuration](../../src/config.py), +explain the missing feature, and finish. Do not implement the missing workflow, +search private code, or keep retrying unsupported commands. + +Resolve the variant CSV, checkpoint, and output directory from the request and +available files. Validate inputs before inference. Execution requires the +project's ML dependencies and a compatible NVIDIA GPU. If a required resource +is unavailable, return the validated inputs where possible and a command with +the missing prerequisite identified. When scoring is requested and resources +are ready, execute and verify the score arrays. A request for preparation ends +with the inputs and command. If variants are missing, report the required +schema; do not invent variants or silently switch to a public dataset. + +Default to the public 80M checkpoint for demonstrations: +`nvidia/NV-CodonFM-Encodon-80M-v1`, revision +`399ca9fe17b57941a7bebc6788033919b417413c`, file +`NV-CodonFM-Encodon-80M-v1.safetensors` and sibling `config.json`. +Reuse an existing checkpoint or download it when needed for the requested work. +Preserve an explicitly requested model size. + ## Preflight 1. Confirm `src/runner.py`, `src/data/mutation_dataset.py`, and @@ -17,8 +41,8 @@ signal, not a clinical diagnosis or an expression-direction prediction. 2. Accept only `encodon_80m`, `encodon_600m`, or `encodon_1b` as `--model_name`. The public parser lists larger names, but its model configuration does not implement them. -3. Require a `.ckpt` file, or a `.safetensors` file with sibling - `config.json`. +3. For model execution, require a `.ckpt` file, or a `.safetensors` file with + sibling `config.json`. Input preparation can use a planned path. 4. Validate the CSV headers before starting a GPU job. ## Inputs @@ -44,16 +68,17 @@ ref_seq[3 * codon_position : 3 * codon_position + 3] == ref_codon The public extractor asserts the second condition and otherwise stops the job. -## Run +## Examples -First validate configuration with `--dryrun`: +Set `CODONFM_DATA_PATH` to the variant CSV, `CODONFM_CHECKPOINT_PATH` to the +checkpoint, and `CODONFM_RUN_DIR` to your chosen output directory: ```bash python -m src.runner eval \ --exp_name variant_scoring \ - --model_name encodon_1b \ - --checkpoint_path /path/to/encodon_1b.safetensors \ - --data_path /path/to/variants.csv \ + --model_name encodon_80m \ + --checkpoint_path "$CODONFM_CHECKPOINT_PATH" \ + --data_path "$CODONFM_DATA_PATH" \ --process_item mutation_pred_mlm \ --dataset_name MutationDataset \ --task_type mutation_prediction \ @@ -61,14 +86,24 @@ python -m src.runner eval \ --mask_mutation \ --num_nodes 1 \ --num_gpus 1 \ - --out_dir /path/to/run \ - --predictions_output_dir /path/to/run/predictions \ - --dryrun + --num_workers 0 \ + --val_batch_size 2 \ + --out_dir "$CODONFM_RUN_DIR" \ + --predictions_output_dir "$CODONFM_RUN_DIR/predictions" ``` Do not remove `--mask_mutation`: without it, the reference codon remains -visible at the scored position and invalidates masked-codon LLR scoring. After -the dry run succeeds, rerun the same command without `--dryrun`. +visible at the scored position and invalidates masked-codon LLR scoring. +For preparation requests, inspect the CSV directly against the input schema +and reference-position checks above, then report the rows checked and provide +the scoring command. Extra columns are allowed; use `--ref_seq_col` if the +reference sequence has a different column name. These checks do not require +the ML runtime. The command above performs inference when resources are ready. + +The existing `--dryrun` optionally builds runtime configuration and skips +execution. It requires the ML dependencies, can create the prediction directory, +and does not read the CSV or load weights. Do not use it as evidence that inputs, +checkpoint compatibility, or prediction quality have been validated. ## Outputs diff --git a/skills/codonfm-score/evals/evals.json b/skills/codonfm-score/evals/evals.json index 05c7fda..d1584f0 100644 --- a/skills/codonfm-score/evals/evals.json +++ b/skills/codonfm-score/evals/evals.json @@ -3,25 +3,35 @@ "evals": [ { "id": "codonfm-score-001", - "prompt": "Use public CodonFM Encodon to score my variants.csv on one GPU.", - "expected_output": "The agent uses mutation_prediction with MutationDataset, masks the mutation, extracts CDS context, and reports all four merged NumPy outputs.", + "prompt": "Validate the supplied variants.csv and prepare an Encodon masked-codon scoring command. Explain the output files and how to interpret the score sign. Use the supplied public source and checkpoint metadata; the two variants are synthetic examples.", + "files": [ + "files/codonfm_source.zip", + "files/encodon_checkpoint.json", + "files/variants.csv" + ], + "expected_output": "Two validated variant rows and a public mutation_prediction command, with the correct output contract and reference-versus-alternate score interpretation.", "assertions": [ - "The command includes --task_type mutation_prediction", - "The command includes --process_item mutation_pred_mlm and --dataset_name MutationDataset", - "The command includes --mask_mutation, --extract-seq, and --num_gpus 1", - "The agent runs --dryrun before the real evaluation", - "The agent lists ref_likelihoods_merged.npy, alt_likelihoods_merged.npy, likelihood_ratios_merged.npy, and ids_merged.npy" + "The command uses mutation_prediction, mutation_pred_mlm, and MutationDataset", + "The command includes --mask_mutation, --extract-seq, --checkpoint_path, and explicit prediction/output paths", + "The agent validates both variant rows, including reference codon agreement at the zero-based CDS position", + "The response identifies ref_likelihoods_merged.npy, alt_likelihoods_merged.npy, likelihood_ratios_merged.npy, and ids_merged.npy as expected inference outputs", + "The agent defines LLR as log p(ref_codon) minus log p(alt_codon), does not equate it with expression direction or clinical effect, and does not fabricate scores" ], "expected_skill": "codonfm-score", "expected_script": null }, { "id": "codonfm-score-002", - "prompt": "Run CodonFM synonymous-codon-aggregated missense_prediction on the public repository.", - "expected_output": "The agent explains that public v1 supports only the general ref/alt LLR and does not silently replace the requested aggregation method.", + "prompt": "Can I run synonymous-codon-aggregated missense_prediction with public CodonFM? Check the supplied source and explain how this relates to the available variant-scoring method.", + "files": [ + "files/codonfm_source.zip", + "files/encodon_checkpoint.json" + ], + "expected_output": "The agent explains that the aggregation workflow is unavailable and distinguishes it from ordinary masked reference/alternate codon LLR scoring.", "assertions": [ - "The agent does not run missense_prediction or missense_inference", - "The agent distinguishes general mutation_prediction from synonymous-codon aggregation" + "The agent identifies missense_prediction and missense_inference as unavailable in the supplied public implementation", + "The agent cites inspected source and distinguishes general mutation_prediction from synonymous-codon aggregation", + "The agent answers the compatibility question without implementing a missing feature or silently substituting the available method" ], "expected_skill": "codonfm-score", "expected_script": null @@ -29,9 +39,10 @@ { "id": "codonfm-score-003", "prompt": "Score this variant.", - "expected_output": "The agent does not assume CodonFM because no model or tool was named.", + "expected_output": "The agent requests the missing variant and analysis context without assuming CodonFM.", "assertions": [ - "The agent does not invoke a codonfm-* skill without CodonFM or Encodon context" + "The agent does not invoke a codonfm-* skill without CodonFM or Encodon context", + "The agent identifies missing inputs without inventing a variant or choosing a model" ], "expected_skill": null, "expected_script": null diff --git a/skills/codonfm-score/evals/files/codonfm_source.zip b/skills/codonfm-score/evals/files/codonfm_source.zip new file mode 100644 index 0000000000000000000000000000000000000000..eb76611c865b4d3638307c06bf685f7a19c59f46 GIT binary patch literal 81758 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 V4ww-X6kgzq61c dict[str, str]: raise AssertionError("SKILL.md is missing YAML frontmatter") result = {} for line in match.group(1).splitlines(): + if line.startswith(" "): + continue key, separator, value = line.partition(":") if not separator: raise AssertionError(f"Invalid frontmatter line: {line}") @@ -95,7 +102,7 @@ def test_frontmatter_and_ui_metadata(self): skill_dir = SKILLS_ROOT / skill_name text = (skill_dir / "SKILL.md").read_text() metadata = _frontmatter(text) - self.assertEqual(set(metadata), {"name", "description"}) + self.assertEqual(set(metadata), {"name", "description", "metadata"}) self.assertEqual(metadata["name"], skill_name) self.assertNotIn("TODO", text) @@ -114,6 +121,55 @@ def test_evals_are_valid_and_named(self): ids = [case["id"] for case in payload["evals"]] self.assertEqual(len(ids), len(set(ids))) + def test_eval_inputs_exist_and_source_fixtures_match_repository(self): + expected_archive = None + for skill_name in sorted(PUBLIC_SKILLS): + evals = SKILLS_ROOT / skill_name / "evals" + payload = json.loads((evals / "evals.json").read_text()) + for case in payload["evals"]: + for relative in case.get("files", []): + path = (evals / relative).resolve() + self.assertTrue(path.is_relative_to(evals.resolve())) + self.assertTrue(path.is_file(), str(path)) + archive_path = evals / "files/codonfm_source.zip" + data = archive_path.read_bytes() + if expected_archive is not None: + self.assertEqual(data, expected_archive, "Trials must receive identical public source") + expected_archive = data + with zipfile.ZipFile(archive_path) as archive: + manifest = json.loads(archive.read("source-manifest.json")) + for name, sha in manifest["sha256"].items(): + self.assertEqual(archive.read(name), (REPO_ROOT / name).read_bytes(), + "Refresh source fixtures with python skills/stage_eval_context.py") + self.assertEqual(hashlib.sha256(archive.read(name)).hexdigest(), sha) + self.assertFalse(any(name.startswith("skills/") or name.endswith(".safetensors") + for name in archive.namelist())) + + def test_ribonn_preparation_preserves_data_without_runtime_dependencies(self): + files = SKILLS_ROOT / "codonfm-finetune/evals/files" + helper = SKILLS_ROOT / "codonfm-finetune/scripts/prepare_ribonn.py" + with tempfile.TemporaryDirectory() as tmp: + output = Path(tmp) / "prepared.csv" + result = subprocess.run([sys.executable, "-S", str(helper), "--input", + str(files / "ribonn_smoke.tsv"), "--output", str(output)], + cwd=REPO_ROOT, capture_output=True, text=True, timeout=10) + self.assertEqual(result.returncode, 0, result.stderr) + report = json.loads(result.stdout) + self.assertEqual(report["split_counts"], {"train": 8, "val": 2, "test": 2}) + self.assertEqual(json.loads(output.with_suffix(".metadata.json").read_text()), report) + with (files / "ribonn_smoke.tsv").open() as handle: + raw = {row["transcript_id"]: row for row in csv.DictReader(handle, delimiter="\t")} + with output.open() as handle: + rows = list(csv.DictReader(handle)) + self.assertEqual(len(rows), 12) + for row in rows: + source = raw[row["id"]] + start, length = int(source["utr5_size"]), int(source["cds_size"]) + self.assertEqual(row["ref_seq"], source["tx_sequence"][start:start + length]) + self.assertEqual(float(row["value"]), float(source["mean_te"])) + fold = int(source["fold"]) + self.assertEqual(row["split"], "val" if fold == 8 else "test" if fold == 9 else "train") + def test_documented_runner_commands_parse(self): parser = _public_runner_parser() commands = [] @@ -136,13 +192,11 @@ def test_fragile_command_requirements(self): )[0] self.assertIn("--mask_mutation", score) self.assertIn("--extract-seq", score) - self.assertIn("--dryrun", score) self.assertEqual(score[score.index("--num_gpus") + 1], "1") embed = _runner_commands( (SKILLS_ROOT / "codonfm-embed/SKILL.md").read_text() )[0] - self.assertIn("--dryrun", embed) self.assertEqual(embed[embed.index("--num_gpus") + 1], "1") finetune = _runner_commands( @@ -155,7 +209,6 @@ def test_fragile_command_requirements(self): "--check_val_every_n_epoch", "--checkpoints_dir", "--use_downstream_head", - "--dryrun", ): self.assertIn(flag, finetune) self.assertEqual( From c6d169d9f942c29f9e595ce518223044acfd1ffc Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Wed, 9 Sep 2026 17:55:35 -0700 Subject: [PATCH 05/13] fix tier1 Signed-off-by: Ohad Mosafi --- .../scripts/prepare_ribonn.py | 24 ++++--- tests/skills/test_public_skills.py | 67 +++++++++++++++++++ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/skills/codonfm-finetune/scripts/prepare_ribonn.py b/skills/codonfm-finetune/scripts/prepare_ribonn.py index 15ca4d1..ed6d76d 100644 --- a/skills/codonfm-finetune/scripts/prepare_ribonn.py +++ b/skills/codonfm-finetune/scripts/prepare_ribonn.py @@ -5,21 +5,23 @@ import argparse import csv +import http.client import io import json import math import time -import urllib.error -import urllib.request from collections import Counter +from contextlib import closing from pathlib import Path DATA_REVISION = "512fca642b6b7b61ae494ad83cfc2b72831636d2" -DATA_URL = ( - f"https://raw.githubusercontent.com/CenikLab/TE_classic_ML/{DATA_REVISION}" +DATA_HOST = "raw.githubusercontent.com" +DATA_PATH = ( + f"/CenikLab/TE_classic_ML/{DATA_REVISION}" "/data/data_with_human_TE_cellline_all_NA_plain.csv" ) +DATA_URL = f"https://{DATA_HOST}{DATA_PATH}" def prepare(handle, max_rows_per_split=8, max_codons=2046, deadline=None): @@ -98,11 +100,15 @@ def main(): with args.input.open(encoding="utf-8-sig", newline="") as handle: rows, report = prepare(handle, args.max_rows_per_split, args.max_codons) else: - # No retry loop or full-dataset download for the default example. + # Fixed HTTPS endpoint with default certificate verification; no redirects or retries. deadline = time.monotonic() + 60 - with urllib.request.urlopen(DATA_URL, timeout=10) as response: - with io.TextIOWrapper(response, encoding="utf-8-sig", newline="") as handle: - rows, report = prepare(handle, args.max_rows_per_split, args.max_codons, deadline) + with closing(http.client.HTTPSConnection(DATA_HOST, timeout=10)) as connection: + connection.request("GET", DATA_PATH) + with connection.getresponse() as response: + if response.status != 200: + raise ValueError(f"Dataset download returned HTTP {response.status}; expected 200 without redirects") + with io.TextIOWrapper(response, encoding="utf-8-sig", newline="") as handle: + rows, report = prepare(handle, args.max_rows_per_split, args.max_codons, deadline) report.update({"source": str(args.input) if args.input else DATA_URL, "upstream_url": DATA_URL}) args.output.parent.mkdir(parents=True, exist_ok=True) with args.output.open("w", newline="", encoding="utf-8") as handle: @@ -110,7 +116,7 @@ def main(): writer.writeheader() writer.writerows(rows) args.output.with_suffix(".metadata.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") - except (ValueError, OSError, urllib.error.URLError) as exc: + except (ValueError, OSError, http.client.HTTPException) as exc: parser.exit(1, f"Preparation failed: {exc}\n") print(json.dumps(report, indent=2)) diff --git a/tests/skills/test_public_skills.py b/tests/skills/test_public_skills.py index 916488f..d6585be 100644 --- a/tests/skills/test_public_skills.py +++ b/tests/skills/test_public_skills.py @@ -11,15 +11,20 @@ import ast import csv import hashlib +import importlib.util +import io import json import re import shlex +import ssl import subprocess import sys import tempfile import unittest import zipfile +from contextlib import redirect_stderr, redirect_stdout from pathlib import Path +from unittest.mock import patch REPO_ROOT = Path(__file__).resolve().parents[2] @@ -86,6 +91,14 @@ def _evaluate_function(namespace): return namespace["evaluate"] +def _ribonn_helper(): + path = SKILLS_ROOT / "codonfm-finetune/scripts/prepare_ribonn.py" + spec = importlib.util.spec_from_file_location("prepare_ribonn", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + class PublicSkillContractTests(unittest.TestCase): def test_expected_public_skill_set(self): actual = { @@ -170,6 +183,60 @@ def test_ribonn_preparation_preserves_data_without_runtime_dependencies(self): fold = int(source["fold"]) self.assertEqual(row["split"], "val" if fold == 8 else "test" if fold == 9 else "train") + def test_ribonn_download_requires_direct_https_success(self): + helper = _ribonn_helper() + fixture = (SKILLS_ROOT / "codonfm-finetune/evals/files/ribonn_smoke.tsv").read_bytes() + for status in (200, 301, 302, 303, 307, 308, 404, 500): + with self.subTest(status=status), tempfile.TemporaryDirectory() as tmp: + output = Path(tmp) / "prepared.csv" + response = io.BytesIO(fixture) + response.status = status + with ( + patch.object(helper.http.client, "HTTPSConnection") as https, + patch.object(sys, "argv", ["prepare_ribonn.py", "--output", str(output)]), + redirect_stdout(io.StringIO()) as stdout, + redirect_stderr(io.StringIO()) as stderr, + ): + connection = https.return_value + connection.getresponse.return_value = response + if status == 200: + helper.main() + report = json.loads(stdout.getvalue()) + self.assertEqual(report["split_counts"], {"train": 8, "val": 2, "test": 2}) + self.assertEqual(report["source"], helper.DATA_URL) + self.assertTrue(output.is_file()) + else: + with self.assertRaises(SystemExit) as failure: + helper.main() + self.assertEqual(failure.exception.code, 1) + self.assertIn(f"HTTP {status}", stderr.getvalue()) + self.assertEqual(list(Path(tmp).iterdir()), []) + https.assert_called_once_with("raw.githubusercontent.com", timeout=10) + connection.request.assert_called_once_with("GET", helper.DATA_PATH) + connection.close.assert_called_once_with() + self.assertTrue(response.closed) + + def test_ribonn_download_handles_transport_errors_without_output(self): + helper = _ribonn_helper() + errors = (TimeoutError("read timed out"), ssl.SSLCertVerificationError("untrusted certificate"), + helper.http.client.BadStatusLine("invalid HTTP response")) + for error in errors: + with self.subTest(error=type(error).__name__), tempfile.TemporaryDirectory() as tmp: + output = Path(tmp) / "prepared.csv" + with ( + patch.object(helper.http.client, "HTTPSConnection") as https, + patch.object(sys, "argv", ["prepare_ribonn.py", "--output", str(output)]), + redirect_stderr(io.StringIO()) as stderr, + ): + https.return_value.getresponse.side_effect = error + with self.assertRaises(SystemExit) as failure: + helper.main() + self.assertEqual(failure.exception.code, 1) + self.assertIn("Preparation failed:", stderr.getvalue()) + self.assertEqual(list(Path(tmp).iterdir()), []) + https.assert_called_once() + https.return_value.close.assert_called_once_with() + def test_documented_runner_commands_parse(self): parser = _public_runner_parser() commands = [] From 3f95e4e01604dbe7fda83d5563ef7a55d3134772 Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Thu, 10 Sep 2026 16:21:09 -0700 Subject: [PATCH 06/13] fix prompts due to guardrails Signed-off-by: Ohad Mosafi --- skills/codonfm-score/evals/evals.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/codonfm-score/evals/evals.json b/skills/codonfm-score/evals/evals.json index d1584f0..3f8b299 100644 --- a/skills/codonfm-score/evals/evals.json +++ b/skills/codonfm-score/evals/evals.json @@ -3,7 +3,7 @@ "evals": [ { "id": "codonfm-score-001", - "prompt": "Validate the supplied variants.csv and prepare an Encodon masked-codon scoring command. Explain the output files and how to interpret the score sign. Use the supplied public source and checkpoint metadata; the two variants are synthetic examples.", + "prompt": "Review the public CodonFM software using the supplied synthetic test fixtures. Inspect codonfm_source.zip and encodon_checkpoint.json, validate both example rows in variants.csv, and prepare an Encodon masked-codon scoring command that matches the supplied implementation.\n\nExplain the expected output files and how to interpret positive and negative scores, citing the relevant source. Provide the command without running inference or inventing numerical results.", "files": [ "files/codonfm_source.zip", "files/encodon_checkpoint.json", @@ -22,7 +22,7 @@ }, { "id": "codonfm-score-002", - "prompt": "Can I run synonymous-codon-aggregated missense_prediction with public CodonFM? Check the supplied source and explain how this relates to the available variant-scoring method.", + "prompt": "Review the supplied public CodonFM source and checkpoint metadata for a software compatibility question: does this implementation support running synonymous-codon-aggregated missense_prediction?\n\nCite the relevant source locations and explain how that workflow relates to the available variant-scoring method. Base the answer on what the supplied implementation actually supports. This task requires source inspection and explanation; no new implementation or inference run is needed.", "files": [ "files/codonfm_source.zip", "files/encodon_checkpoint.json" From 360b8a5428b7ed439f16307019e0b7424448acee Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Thu, 17 Sep 2026 10:45:44 -0700 Subject: [PATCH 07/13] reconfig harbor Signed-off-by: Ohad Mosafi --- skills/codonfm-score/SKILL.md | 33 ++++++++++++++++++++++++--- skills/codonfm-score/evals/config.yml | 6 +++++ skills/codonfm-score/evals/evals.json | 1 + 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 skills/codonfm-score/evals/config.yml diff --git a/skills/codonfm-score/SKILL.md b/skills/codonfm-score/SKILL.md index 2bd51f6..f7ff434 100644 --- a/skills/codonfm-score/SKILL.md +++ b/skills/codonfm-score/SKILL.md @@ -1,6 +1,6 @@ --- name: codonfm-score -description: Score synonymous or missense coding variants with public CodonFM Encodon checkpoints using masked-codon reference-versus-alternate log-likelihood ratios. Use when a user explicitly asks for CodonFM or Encodon zero-shot variant scoring. Support the public mutation_prediction workflow only; reject Decodon and the newer synonymous-codon-aggregated missense_prediction workflow because they are not present in public CodonFM v1. +description: Validate, prepare, or run public CodonFM Encodon masked-codon variant scoring and review compatibility of its scoring workflows. Use only when the user explicitly requests CodonFM or Encodon, or that context is already established in the conversation. Do not select this skill for a generic variant-scoring request without that context; ask for the variant and intended analysis first. metadata: author: "NVIDIA BioNeMo " --- @@ -12,6 +12,22 @@ signal, not a clinical diagnosis or an expression-direction prediction. ## Instructions +First confirm CodonFM or Encodon context in the user's request or established +conversation. If that context is missing, ask for any missing variant details +and the intended analysis before choosing a model or inspecting model-specific +files. The presence of this skill or source files alone does not establish +the user's intent. + +For source reviews and command preparation, inspect the supplied source and +metadata without installing the ML runtime. Use an available Python 3 +interpreter with standard-library `zipfile`, `json`, and `csv`; do not assume +the `python` alias or `unzip` exists. Read archive members directly with +`ZipFile.namelist()` and `ZipFile.read()` where possible. If extraction is +needed, use a fresh directory from `tempfile.mkdtemp()` or `mktemp -d` and +preserve existing checkouts and scratch directories. Check whether `rg` is +available; use `grep` or Python if it is absent. Read the source sections +needed for the requested command or compatibility question. + Check whether the request is executable in public v1 before installing or downloading anything. For synonymous-codon aggregation or Decodon, inspect the [parser](../../src/runner.py) and [model configuration](../../src/config.py), @@ -73,6 +89,10 @@ The public extractor asserts the second condition and otherwise stops the job. Set `CODONFM_DATA_PATH` to the variant CSV, `CODONFM_CHECKPOINT_PATH` to the checkpoint, and `CODONFM_RUN_DIR` to your chosen output directory: +Use the interpreter from the configured ML environment for inference. The +example uses `python`; substitute that environment's interpreter path if the +alias is unavailable. + ```bash python -m src.runner eval \ --exp_name variant_scoring \ @@ -119,6 +139,15 @@ LLR is `log p(ref_codon) - log p(alt_codon)`; a larger positive value means the alternate codon is less probable in context. It does not say whether expression goes up or down. +## Reporting + +Keep the final answer concise and self-contained, with the requested command +or compatibility conclusion near the start. For command preparation, include +each row's validation result, the complete command, all four output filenames, +and the LLR definition and sign interpretation. Cite the inspected source +locations for the command, outputs, and scoring semantics. State whether +inference ran; report numerical scores only when execution produced them. + ## Boundaries - General `mutation_prediction` handles both synonymous and missense changes. @@ -128,5 +157,3 @@ expression goes up or down. - If a user asks specifically for synonymous-codon-aggregated missense scoring, explain that public v1 only provides the general ref/alt LLR. Do not silently substitute the two methods. -- Do not invoke this skill for a bare “score this variant” request that does - not name CodonFM or Encodon. diff --git a/skills/codonfm-score/evals/config.yml b/skills/codonfm-score/evals/config.yml new file mode 100644 index 0000000..31036d7 --- /dev/null +++ b/skills/codonfm-score/evals/config.yml @@ -0,0 +1,6 @@ +schema_version: 1 + +harbor: + agents: + claude-code: + model: aws/anthropic/bedrock-claude-opus-5 diff --git a/skills/codonfm-score/evals/evals.json b/skills/codonfm-score/evals/evals.json index 3f8b299..5614d84 100644 --- a/skills/codonfm-score/evals/evals.json +++ b/skills/codonfm-score/evals/evals.json @@ -39,6 +39,7 @@ { "id": "codonfm-score-003", "prompt": "Score this variant.", + "files": [], "expected_output": "The agent requests the missing variant and analysis context without assuming CodonFM.", "assertions": [ "The agent does not invoke a codonfm-* skill without CodonFM or Encodon context", From 842a9c27f810ac40518b9794fdb8f3e47c2d540b Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Thu, 17 Sep 2026 12:58:08 -0700 Subject: [PATCH 08/13] run Opus5 for codonfm-setup Signed-off-by: Ohad Mosafi --- skills/codonfm-setup/evals/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 skills/codonfm-setup/evals/config.yml diff --git a/skills/codonfm-setup/evals/config.yml b/skills/codonfm-setup/evals/config.yml new file mode 100644 index 0000000..31036d7 --- /dev/null +++ b/skills/codonfm-setup/evals/config.yml @@ -0,0 +1,6 @@ +schema_version: 1 + +harbor: + agents: + claude-code: + model: aws/anthropic/bedrock-claude-opus-5 From 29194e8d0e6531f4e01dac0182021e93c13642ad Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Fri, 18 Sep 2026 10:39:16 -0700 Subject: [PATCH 09/13] tier3 skill fixes Signed-off-by: Ohad Mosafi --- skills/codonfm-setup/SKILL.md | 223 +++++++++++++++++++++++++--------- 1 file changed, 163 insertions(+), 60 deletions(-) diff --git a/skills/codonfm-setup/SKILL.md b/skills/codonfm-setup/SKILL.md index 5b343f9..9cee52b 100644 --- a/skills/codonfm-setup/SKILL.md +++ b/skills/codonfm-setup/SKILL.md @@ -12,33 +12,82 @@ public v1 code and public Encodon checkpoints. ## Instructions -Determine whether the user wants instructions, a downloaded checkpoint, or a -working model environment. Inspect the [runner](../../src/runner.py), -[model configuration](../../src/config.py), [Dockerfile](../../Dockerfile), -[launcher](../../run_dev.sh), and [requirements](../../requirements.txt). -Reuse available environments and checkpoints, and choose paths from the user's -project. Follow the requested scope: instructions do not require installation; -checkpoint downloads do not require a GPU; actual model execution requires the -ML dependencies and a compatible NVIDIA GPU. - -Check hardware before installing the runtime: use `nvidia-smi` if available, or -check CUDA through an existing PyTorch installation. If a prerequisite cannot -be met, complete independent setup steps and report what is still missing. -Check supplied files and configuration directly when preparing setup instructions. -The existing runner's optional `--dryrun` builds runtime configuration with -the ML dependencies installed, then stops before execution. It does not validate -CSV data or load weights, and setup instructions do not require running it. -Public Decodon is unavailable: inspect local source and explain the boundary -without attempting an unsupported installation. - -## Preflight +1. Determine whether the user wants instructions, a downloaded checkpoint, a + working model environment, or a combination of these. +2. For setup instructions or runtime work, inspect the supplied files and + configuration directly: the [runner](../../src/runner.py), + [model configuration](../../src/config.py), [Dockerfile](../../Dockerfile), + [launcher](../../run_dev.sh), and [requirements](../../requirements.txt). + Runtime setup requires a checkout; a supplied source archive is sufficient + for preparing instructions. +3. Check the [public-v1 boundaries](#public-v1-boundaries). For an unsupported + request, inspect `MODEL_ARCHITECTURES` in `src/config.py`, the model modules, + and any requested script before explaining the boundary and ending that + path. Runner argument choices alone do not establish implementation support. +4. Reuse available environments and checkpoints, and choose explicit paths + from the user's project. +5. Follow only the requested paths below. Environment setup alone does not + require a checkpoint download; download weights only when the request needs + them and a suitable local checkpoint is unavailable. + +| Requested scope | Action and completion condition | +| --- | --- | +| Instructions only | Inspect the supplied source/configuration, provide the commands described under Reporting setup instructions, then stop. No installation or GPU verification is required. | +| Checkpoint only | Follow Download a checkpoint, check the downloaded files, report their paths, then stop. No Docker, GPU, or model runtime is required. | +| Working model environment | Follow Runtime preflight, choose the container or direct-host path, then Verify the runtime. Report the checks performed and any remaining limitations. | + +For supplied source archives, inspect selected files with the available Python +3 standard library (`zipfile.ZipFile.namelist()` and `read()`) without extracting +the whole archive. If extraction is needed, use a fresh directory from +`mktemp -d` or `tempfile.mkdtemp()`. Preserve existing checkouts and temporary +directories; do not delete or overwrite them to prepare a source inspection. + +The runner's optional `--dryrun` requires the ML dependencies to be installed +already. It constructs runtime configuration, then stops before execution. +It does not install packages, validate CSV data, or load weights. Preparing +setup instructions does not require running it. + +## Reporting setup instructions + +For instruction requests, put complete commands for the requested setup path +early in a compact, self-contained answer, even when also writing a guide file. + +- For downloads, use supplied checkpoint metadata for the exact repository, + revision, weight filename, and `config.json`. Show the destination directory + and keep the weights and configuration together. +- For containers, state Docker/GPU prerequisites, explain existing-container + replacement before the launcher command, and show explicit host data and + checkpoint paths and the checkpoint mount at `/data/checkpoints`. +- For direct-host setup, include `python3.11 -m venv`, + `python -m pip install -r requirements.txt`, a writable `MPLCONFIGDIR`, + `torch.cuda.is_available()` verification, and explicit host checkpoint paths. +- State which checks actually ran and what remains unverified before model + execution. Written instructions alone do not establish a working environment. + +For a compatibility-only question, give the source-backed availability answer +without adding an unrelated installation procedure. + +## Runtime preflight + +For a working environment, check hardware before installing the runtime: use +`nvidia-smi` if available, or check CUDA through an existing PyTorch installation. +Actual model execution requires the ML dependencies and a compatible NVIDIA GPU. +Compare the driver with the CUDA version required by the selected runtime using +[NVIDIA's compatibility guidance](https://docs.nvidia.com/deploy/cuda-compatibility/minor-version-compatibility.html). +For the Dockerfile's `nvcr.io/nvidia/pytorch:24.10-py3` base, also check the +[24.10 driver requirements](https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes/rel-24-10.html#driver-requirements). +If a prerequisite is missing, follow Failure handling below. + +### Container preflight 1. Confirm `Dockerfile`, `run_dev.sh`, and `src/runner.py` exist. -2. For container execution, confirm `docker info` succeeds and `nvidia-smi` - sees the intended GPU. Direct-host setup does not require Docker. +2. Confirm `docker info` succeeds. Docker must have NVIDIA Container Toolkit + configured for `--gpus all`; host GPU visibility alone does not establish + container GPU access. Verify access in the launched container below. 3. Run `bash -n run_dev.sh` before launching it. -4. Resolve explicit host paths for data and checkpoints. Do not rely on the - `/data/codonfm` defaults unless the user confirms they exist. +4. Resolve existing absolute host paths for data and checkpoints. Always pass + both path flags to the launcher rather than relying on `/data/codonfm` + defaults. Create missing project directories only as needed for the request. 5. Check for an existing container before launch: ```bash @@ -48,53 +97,86 @@ docker ps -a --filter name='^/codon-fm-dev-container$' If an exact-name container is running, `run_dev.sh` stops and removes it; tell the user before replacement. If it is stopped, the script cannot reuse the name, so obtain confirmation before removing it with -`docker rm codon-fm-dev-container`. The public script also uses host -networking/IPC and mounts the user's SSH directory read-only; disclose this -before execution. +`docker rm codon-fm-dev-container`. If removal is declined, preserve the +container, skip this launch, and report the name conflict. + +The public script uses host networking/IPC and mounts the user's SSH directory +read-only; disclose this before execution. It has no opt-out flags for these +settings. If they conflict with the user's constraints, use the direct-host +path when feasible; otherwise report that container launch remains blocked. ## Build and launch +Set `CODONFM_REPO_DIR`, `CODONFM_DATA_DIR`, and `CODONFM_CHECKPOINT_DIR` to +existing absolute paths chosen for the project. + ```bash -cd "$CODONFM_REPO_DIR" +cd "${CODONFM_REPO_DIR:?Set the repository path}" bash run_dev.sh \ - --data-dir "$CODONFM_DATA_DIR" \ - --checkpoints-dir "$CODONFM_CHECKPOINT_DIR" + --data-dir "${CODONFM_DATA_DIR:?Set the host data path}" \ + --checkpoints-dir "${CODONFM_CHECKPOINT_DIR:?Set the host checkpoint path}" ``` The host checkpoint directory is mounted at `/data/checkpoints` inside the container. The image is `codon-fm-dev`; the container is `codon-fm-dev-container`. -Set `CODONFM_REPO_DIR`, `CODONFM_DATA_DIR`, and `CODONFM_CHECKPOINT_DIR` to -existing absolute paths chosen for the project. - Use only the checked-in public code and the dependency versions declared in -its `Dockerfile` and `requirements.txt`. +its `Dockerfile` and `requirements.txt`. Continue to Verify the runtime after +launch; checkpoint downloads are a separate requested action. ## Run directly without Docker -Use this path when Docker is unavailable and the host has a compatible NVIDIA -driver. The example below uses Python 3.11, CUDA-capable PyTorch, and one GPU. -Operate from a writable checkout and use a dedicated virtual environment: +Use this path when the user prefers host execution or Docker is unavailable. +It requires a compatible NVIDIA driver, Python 3.11 for the commands below, +and a writable checkout. Confirm `python3.11 --version` succeeds before +installation. Reuse a compatible project environment; otherwise create a +dedicated virtual environment. Set `CODONFM_CACHE_DIR` to a writable cache +directory before running these commands: ```bash -cd "$CODONFM_REPO_DIR" +cd "${CODONFM_REPO_DIR:?Set the repository path}" python3.11 -m venv .venv . .venv/bin/activate python -m pip install --upgrade pip python -m pip install -r requirements.txt -mkdir -p "$CODONFM_CACHE_DIR/matplotlib" +mkdir -p "${CODONFM_CACHE_DIR:?Set a writable cache path}/matplotlib" export MPLCONFIGDIR="$CODONFM_CACHE_DIR/matplotlib" -python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))" +python -c "import sys, torch; available = torch.cuda.is_available(); \ +print(available, torch.cuda.get_device_name(0) if available else 'CUDA unavailable'); \ +sys.exit(0 if available else 1)" ``` -Set `CODONFM_CACHE_DIR` to a writable cache directory. Expect `True` and the -selected GPU name. The requirements file configures the -CUDA 12.4 PyTorch index for xFormers. Use explicit host paths in all subsequent -runner commands; unlike the container path, no `/data/checkpoints` mount is -created. +The last command is the direct-host GPU verification; interpret it as described +under Verify the runtime. The requirements file configures the CUDA 12.4 +PyTorch index for xFormers. Use explicit host paths in subsequent runner +commands; no `/data/checkpoints` mount is created on this path. -## Examples +## Download a checkpoint + +Run only for a requested checkpoint. Reuse a suitable local copy first. +Check `hf --help` and `hf download --help` in the environment that will perform +the download. If the CLI is missing, use a separate download virtual environment +and `python -m pip install huggingface_hub`; preserve the model environment's +dependency versions. The [CLI documentation](https://huggingface.co/docs/huggingface_hub/en/guides/cli) +describes installation and supported options. Public ungated downloads do not +require `hf auth login`. + +Set `CODONFM_CHECKPOINT_DIR` to an absolute writable directory in the environment +running `hf`: the chosen host checkpoint root on the host, or `/data/checkpoints` +inside the launched container. Host shell variables are not automatically set +inside the container. Use supplied metadata for exact filenames and revisions; +keep the weights and `config.json` together. + +For the public 1B checkpoint: + +```bash +hf download nvidia/NV-CodonFM-Encodon-1B-v1 \ + NV-CodonFM-Encodon-1B-v1.safetensors config.json \ + --local-dir "${CODONFM_CHECKPOINT_DIR:?Set the checkpoint root}/encodon-1b" +``` + +### Small checkpoint example For a small demonstration, prefer the original public Encodon 80M weights: @@ -102,7 +184,7 @@ For a small demonstration, prefer the original public Encodon 80M weights: hf download nvidia/NV-CodonFM-Encodon-80M-v1 \ NV-CodonFM-Encodon-80M-v1.safetensors config.json \ --revision 399ca9fe17b57941a7bebc6788033919b417413c \ - --local-dir "$CODONFM_CHECKPOINT_DIR/encodon-80m" + --local-dir "${CODONFM_CHECKPOINT_DIR:?Set the checkpoint root}/encodon-80m" ``` The [checkpoint](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1/tree/main) @@ -112,15 +194,6 @@ use TransformerEngine in `bionemo-recipes`; use the original model IDs with this public CodonFM codebase. Download only the weights and `config.json`, and reuse an existing local checkpoint. -## Download a checkpoint - -Run inside the container, or in another environment with Hugging Face Hub: - -```bash -hf download nvidia/NV-CodonFM-Encodon-1B-v1 \ - --local-dir /data/checkpoints/encodon-1b -``` - Other supported public model IDs are: - `nvidia/NV-CodonFM-Encodon-80M-v1` @@ -134,15 +207,45 @@ training property, not a separate architecture. For `.safetensors`, keep `config.json` in the same directory as the model file. Never invent a Decodon or undocumented checkpoint path. -## Verify +After a successful download, confirm the expected files exist, `config.json` +parses, and any supplied byte size or checksum matches. Report the absolute +file paths and revision. A checkpoint-only request ends here; it does not +continue to GPU verification. For a combined request, continue only the other +requested path. + +## Verify the runtime + +This section applies only to working-environment requests. For direct-host +execution, use the GPU check at the end of Run directly without Docker in the +model's activated environment. For a running container, use a host terminal: ```bash docker exec codon-fm-dev-container python -c \ - "import torch; print(torch.cuda.is_available())" + "import sys, torch; available = torch.cuda.is_available(); \ +print(available, torch.cuda.get_device_name(0) if available else 'CUDA unavailable'); \ +sys.exit(0 if available else 1)" ``` -Expect `True` on a configured NVIDIA GPU host. If Docker or a GPU is -unavailable, report the missing prerequisite; do not claim setup succeeded. +Expect `True`, a GPU name, and exit status zero. `False` or an exception means +runtime verification failed; report the missing prerequisite or error. A CUDA +check establishes GPU access, not successful checkpoint loading or model +execution. Finish the environment request by reporting the verified runtime, +available checkpoint paths, and any checks that remain unperformed. + +## Failure handling + +- If a command fails, diagnose the reported cause. Retry an unchanged command + at most once for a transient failure, such as a download timeout. For a + persistent failure, stop that path and report the error and needed fix. +- For failed downloads, preserve the cache and partial files, retry the same + supported command when appropriate, and report which files remain missing + or unverified. Do not invent retry flags or claim an incomplete download + succeeded. +- If runtime prerequisites or container constraints cannot be met, complete + independent work within the request: inspect supplied source/configuration, + prepare setup commands, or download a requested checkpoint when possible. + Report completed work and the unmet prerequisites; do not claim the runtime + is working. ## Public-v1 boundaries From 7a3c7cbb97e6c2dd4d3465870cb4ea3c25c19e96 Mon Sep 17 00:00:00 2001 From: nvskills-svc-account Date: Fri, 2 Oct 2026 01:05:20 +0000 Subject: [PATCH 10/13] Attach NVSkills validation signatures Signed-off-by: nvskills-svc-account --- skills/codonfm-embed/BENCHMARK.md | 119 ++++++++++++++++++++++++ skills/codonfm-embed/skill-card.md | 87 ++++++++++++++++++ skills/codonfm-embed/skill.oms.sig | 1 + skills/codonfm-finetune/BENCHMARK.md | 123 +++++++++++++++++++++++++ skills/codonfm-finetune/skill-card.md | 88 ++++++++++++++++++ skills/codonfm-finetune/skill.oms.sig | 1 + skills/codonfm-score/BENCHMARK.md | 125 ++++++++++++++++++++++++++ skills/codonfm-score/skill-card.md | 87 ++++++++++++++++++ skills/codonfm-score/skill.oms.sig | 1 + skills/codonfm-setup/BENCHMARK.md | 123 +++++++++++++++++++++++++ skills/codonfm-setup/skill-card.md | 88 ++++++++++++++++++ skills/codonfm-setup/skill.oms.sig | 1 + 12 files changed, 844 insertions(+) create mode 100644 skills/codonfm-embed/BENCHMARK.md create mode 100644 skills/codonfm-embed/skill-card.md create mode 100644 skills/codonfm-embed/skill.oms.sig create mode 100644 skills/codonfm-finetune/BENCHMARK.md create mode 100644 skills/codonfm-finetune/skill-card.md create mode 100644 skills/codonfm-finetune/skill.oms.sig create mode 100644 skills/codonfm-score/BENCHMARK.md create mode 100644 skills/codonfm-score/skill-card.md create mode 100644 skills/codonfm-score/skill.oms.sig create mode 100644 skills/codonfm-setup/BENCHMARK.md create mode 100644 skills/codonfm-setup/skill-card.md create mode 100644 skills/codonfm-setup/skill.oms.sig diff --git a/skills/codonfm-embed/BENCHMARK.md b/skills/codonfm-embed/BENCHMARK.md new file mode 100644 index 0000000..0dde7ca --- /dev/null +++ b/skills/codonfm-embed/BENCHMARK.md @@ -0,0 +1,119 @@ +# Skill Benchmark: codonfm-embed + +> **Overall verdict: NEUTRAL — One or more dimensions remain below PASS** + +Live evaluation did not show a material gain or regression. Collect more evidence or improve the skill before making a publication decision. + +## Evaluation Metadata + +- Skill: `codonfm-embed` +- Evaluation date: 2026-10-02 +- Evaluator version: `1.5.6` +- Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`), Codex (`openai/openai/gpt-5.5`) +- Tasks: 2 evaluation tasks (2 positive) +- Dataset digest: `sha256:13e6b2a6ffaa03dba4985cf78c69ce0dfef33331f5fba362890e08c6cf187db2` (skill-evaluator-dataset-snapshot/1) +- Attempts per task: 1 +- Environment: `k8s-sandbox` +- Tier 2 evidence: required for publication +- Tier 3 evidence: required for publication + +Each task attempt ran in its own isolated sandbox pod. + +## What This Report Answers + +The three-tier evaluation checks whether the skill: + +- is safe to use; +- produces correct answers; +- is discovered and activated when needed; +- helps the agent complete the user's goal and expected workflow; and +- avoids wasted skill and tool usage. + +## Results at a Glance + +| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 91.5% — baseline ran, but no comparable score was available; uplift unavailable | 78.0% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 100.0% → 100.0% (±0.0 points) | 0.0% → 50.0% (+50.0 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 92.5% — baseline ran, but no comparable score was available; uplift unavailable | 75.0% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 100.0% → 81.3% (-18.7 points) | 100.0% → 81.3% (-18.7 points) | +| Efficiency | 83.6% — baseline ran, but no comparable score was available; uplift unavailable | 83.9% — baseline ran, but no comparable score was available; uplift unavailable | + +**How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. + +Example: `47.0% → 92.0% (+45.0 points)` means the skill-assisted run scored 92.0%, 45.0 percentage points above its 47.0% no-skill baseline. + +## Token Usage + +Actual Tier 3 execution usage is reported for every observed agent/case pair and both conditions. + +| Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | +|---|---|---:|---:|---:|---:|---| +| claude-code | All cases | 1,552,466 | 2,540,319 | -987,853 | -38.89% | skill 2/2; base 2/2 | +| claude-code | codonfm-embed-001 | 1,191,884 | 1,670,815 | -478,931 | -28.66% | skill 1/1; base 1/1 | +| claude-code | codonfm-embed-002 | 360,582 | 869,504 | -508,922 | -58.53% | skill 1/1; base 1/1 | +| codex | All cases | 756,363 | 747,441 | +8,922 | +1.19% | skill 2/2; base 2/2 | +| codex | codonfm-embed-001 | 347,201 | 309,716 | +37,485 | +12.10% | skill 1/1; base 1/1 | +| codex | codonfm-embed-002 | 409,162 | 437,725 | -28,563 | -6.53% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 2,308,829 | 3,287,760 | -978,931 | -29.78% | skill 4/4; base 4/4 | + +Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. + +## Tier Status + +| Tier | Purpose | Status | Evidence | +|---|---|---|---| +| Tier 1 | Static validation | **PASSED WITH OBSERVATIONS** | 11 validator(s); 7 finding(s) | +| Tier 2 | Semantic deduplication | **PASSED** | 2 validator(s); 0 finding(s) | +| Tier 3 | Live agent evaluation | **NEUTRAL** | 2 agent(s); 2 task(s) | + +## Findings and Observations + +
+Show detailed findings and successful checks + +- **MEDIUM** QUALITY/quality_correctness: SKILL_SPEC recommended field missing: 'metadata.tags' (`skills/codonfm-embed/SKILL.md`) +- **LOW** QUALITY/quality_discoverability: Description very long (373 chars, recommend 50-150) (`skills/codonfm-embed/SKILL.md`) +- **LOW** QUALITY/quality_discoverability: No '## Purpose' section (`skills/codonfm-embed/SKILL.md`) +- **LOW** QUALITY/quality_reliability: No prerequisites/requirements documented (`skills/codonfm-embed/SKILL.md`) +- **LOW** QUALITY/quality_reliability: No limitations documented (`skills/codonfm-embed/SKILL.md`) +- 2 additional finding(s) are available in the full evaluation artifacts. + +
+ +## Scoring Methodology + +
+Show dimension definitions, source signals, and thresholds + +| Dimension | Question | Scored signals | +|---|---|---| +| Security | Is it safe to use? | `security` (100%) | +| Correctness | Is the answer correct? | `accuracy` (100%) | +| Discoverability | Was the right skill loaded when needed? | `skill_execution` (100%) | +| Effectiveness | Did the skill help complete the task? | `goal_accuracy` (50%) + `behavior_check` (50%) | +| Efficiency | Did it avoid wasted tool calls and token usage? | `skill_efficiency` (50%) + `token_efficiency` (50%) | + +- Dimension bands: PASS at 50% or above; NEUTRAL from 40% to below 50%; FAIL below 40%. +- Overall Tier 3 lift: PASS at +5 points or more; FAIL at -10 points or less; values between those bands are NEUTRAL. +- Overall verdict: PASS only when every configured dimension passes for at least one supported agent. Lift is reported as diagnostic evidence and does not override this gate. +- The 50% attempt pass threshold is a separate per-task gate; it is not the dimension pass threshold. +- Effectiveness is the equal-weight mean of goal completion (`goal_accuracy`) and expected workflow adherence (`behavior_check`). +- Efficiency is 50% tool-call productivity (the backward-compatible `skill_efficiency` wire id) and 50% `token_efficiency`. Positive-case skill routing is scored under Discoverability, not Efficiency; a negative case without a routing target is N/A. N/A sources are omitted, remaining weights are renormalized, and the dimension is marked partial. + +Signals present in this run: + +- `security` (Security): unsafe operations, secret leakage, and unauthorized access. +- `skill_execution` (Skill Execution): whether the expected skill was selected, decoys were avoided, and the workflow executed. +- `skill_efficiency` (Tool Productivity): tool-call productivity (legacy wire id; routing is scored under Discoverability). +- `accuracy` (Accuracy): final-answer correctness against the reference answer. +- `goal_accuracy` (Goal Accuracy): whether the user's goal was achieved. +- `behavior_check` (Behavior Check): whether the expected workflow behavior was followed. +- `token_efficiency` (Token Efficiency): actual uncached prompt plus completion usage (50% of Efficiency). + +
+ +## Freshness + +Regenerate this benchmark when the skill, evaluation dataset, target agent/model, evaluator version, environment, or scoring policy changes. diff --git a/skills/codonfm-embed/skill-card.md b/skills/codonfm-embed/skill-card.md new file mode 100644 index 0000000..c9708dc --- /dev/null +++ b/skills/codonfm-embed/skill-card.md @@ -0,0 +1,87 @@ +## Description:
+Extract frozen CLS embeddings from public CodonFM Encodon checkpoints for coding-sequence property modeling.
+ +This skill is ready for commercial/non-commercial use.
+ +## Owner +NVIDIA
+ +### License/Terms of Use:
+Apache 2.0
+## Use Case:
+Developers and bioinformatics engineers who need to extract frozen CLS vector embeddings from public CodonFM Encodon checkpoints for downstream codon-sequence property modeling tasks such as translation efficiency, expression, or mRNA stability prediction.
+ +### Deployment Geography for Use:
+Global
+ +## Requirements / Dependencies:
+**Requires API Key or External Credential:** [Not Specified]
+**Credential Type(s):** [None identified]
+ +Do not include secrets in prompts/logs/output; use least-privilege credentials; rotate keys as appropriate.
+ +## Known Risks and Mitigations:
+Risk: Review before execution as proposals could introduce incorrect or misleading guidance into skills.
+Mitigation: Review and scan skill before deployment.
+ +## Reference(s):
+- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
+- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
+- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
+- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
+- [CodonFM Encodon on NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
+ + +## Skill Output:
+**Output Type(s):** [Files, Shell commands]
+**Output Format:** [NumPy arrays (.npy) and Markdown with inline bash code blocks]
+**Output Parameters:** [1D]
+**Other Properties Related to Output:** [Outputs embeddings_merged.npy (shape: rows × hidden_size) and ids_merged.npy aligned by row index]
+ +## Evaluation Agents Used:
+- Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`)
+- Codex (`openai/openai/gpt-5.5`)
+ + + +## Evaluation Tasks:
+2 evaluation tasks (2 positive) executed in isolated sandbox pods.
+ +## Evaluation Metrics Used:
+Reported benchmark dimensions:
+- Security: Whether the skill is safe to use, checking for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Whether the answer is correct against the reference answer.
+- Discoverability: Whether the right skill was loaded when needed, including skill selection and decoy avoidance.
+- Effectiveness: Whether the skill helped complete the task, measured by goal completion and expected workflow adherence.
+- Efficiency: Whether the skill avoided wasted tool calls and token usage.
+ +Underlying evaluation signals used in this run:
+- `security`: Checks for unsafe operations, secret leakage, and unauthorized access.
+- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
+- `accuracy`: Final-answer correctness against the reference answer.
+- `goal_accuracy`: Whether the user's goal was achieved.
+- `behavior_check`: Whether the expected workflow behavior was followed.
+- `skill_efficiency`: Tool-call productivity.
+- `token_efficiency`: Actual uncached prompt plus completion token usage.
+ + + +## Evaluation Results:
+| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 91.5% — uplift unavailable | 78.0% — uplift unavailable | +| Security | 100.0% → 100.0% (±0.0 points) | 0.0% → 50.0% (+50.0 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 92.5% — uplift unavailable | 75.0% — uplift unavailable | +| Effectiveness | 100.0% → 81.3% (-18.7 points) | 100.0% → 81.3% (-18.7 points) | +| Efficiency | 83.6% — uplift unavailable | 83.9% — uplift unavailable | + +## Skill Version(s):
+29194e8 (source: git SHA, committed 2026-09-18)
+ +## Ethical Considerations:
+NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
+ +(For Release on NVIDIA Platforms Only)
+Please report quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://app.intigriti.com/programs/nvidia/nvidiavdp/detail).
diff --git a/skills/codonfm-embed/skill.oms.sig b/skills/codonfm-embed/skill.oms.sig new file mode 100644 index 0000000..edc369e --- /dev/null +++ b/skills/codonfm-embed/skill.oms.sig @@ -0,0 +1 @@ +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1lbWJlZCIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI0YWMyNGRlODZjMjMxOTkwNjNlZWEwYzdhYThkMzAxMTIzY2NlOGEyN2E1ZGI4MmI0MDc5M2VmZTA3YWY0NDc4IgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmYWU4YWU1NTkzN2RkYmVmOWJhNDMxM2Y2YWZjM2QzNTgwMDNmYzNkNTFmZGM0Y2RjMGI3YmQwNTgxNTQ2NTNmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM4YmFjZWUzOWI0ODk4MmQwMzJjMTZmMDllODgxM2ZmZThiN2FhZjM4Y2FjNGRkNjAxNTVkYjVhMDcyNjU1YjQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViZDkxNWJiM2VkMTFjMDdjZjQzMTc4YTMwYTJmYTJlZDdmZTg5ODA2ZDZlMTI0MThmY2YxODFmNmJjYTMxOTgiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9ldmFscy5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhZjliMjMxYmI2NjBhNTkyMzlmODU5NTc1ODZhNWMzZTNkMDMwZGUxNjEyMzgwZDU2ZGI4MWViNDU2NzU5ODVkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvY29kb25mbV9zb3VyY2UuemlwIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmMGMxMjJmMTlhMmQzMTZmZTk1Y2NmYWEwYWM1MWJkNjEzY2ZhYmRkMDlmZDg5ZmJiNzY2NTYyYTVmMGVlZDI3IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvZW5jb2Rvbl9jaGVja3BvaW50Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy9zZXF1ZW5jZXMuY3N2IiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyZWQ2OGZlZmQ2MWUzNjFkZDIxZDBiYzI4YTdkZGRlZjMxZTNlOWI0MmRkNjEwMGY1MzRlOTA2OTdjNzk3ZTc5IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTZkMTA2MGIzMTAzOGExNjdmNWNmOWQ3MTYxYzYzOWM4M2U3ZDA3ZWI1NzYxNTliNWRiMWIwMjA4MWVlYmM4OSIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAibWV0aG9kIjogImZpbGVzIiwKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGh1YiIsCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0IiwKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiCiAgICAgIF0sCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGQCMCQ5CSeV0bGAa9NEXHN7AbCDGfCDbDZf0OborrK56Y8jpGrpu9f1NGjDzVaVfNCDfAIwZWnJAYQe3lAmBN3XWFNizNXrNcpR/no3w2OZETi3150UtFr8otFqJUh8SLKzJkYi","keyid":""}]}} \ No newline at end of file diff --git a/skills/codonfm-finetune/BENCHMARK.md b/skills/codonfm-finetune/BENCHMARK.md new file mode 100644 index 0000000..04a87d1 --- /dev/null +++ b/skills/codonfm-finetune/BENCHMARK.md @@ -0,0 +1,123 @@ +# Skill Benchmark: codonfm-finetune + +> ✅ **Overall verdict: PASS — Recommended for publication** + +## Publication Recommendation + +Recommended for publication based on the completed evaluation evidence in this report. + +## Evaluation Metadata + +- Skill: `codonfm-finetune` +- Evaluation date: 2026-10-02 +- Evaluator version: `1.5.6` +- Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`), Codex (`openai/openai/gpt-5.5`) +- Tasks: 3 evaluation tasks (3 positive) +- Dataset digest: `sha256:81d6d44379cdd6e73149247223022a3b07d3b00517da29ff30435ddc7aedc045` (skill-evaluator-dataset-snapshot/1) +- Attempts per task: 1 +- Environment: `k8s-sandbox` +- Tier 2 evidence: required for publication +- Tier 3 evidence: required for publication + +Each task attempt ran in its own isolated sandbox pod. + +## What This Report Answers + +The three-tier evaluation checks whether the skill: + +- is safe to use; +- produces correct answers; +- is discovered and activated when needed; +- helps the agent complete the user's goal and expected workflow; and +- avoids wasted skill and tool usage. + +## Results at a Glance + +| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 89.9% — baseline ran, but no comparable score was available; uplift unavailable | 87.9% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 100.0% → 100.0% (±0.0 points) | 33.3% → 66.7% (+33.4 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 87.7% — baseline ran, but no comparable score was available; uplift unavailable | 91.0% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 83.3% → 80.0% (-3.3 points) | 86.7% → 93.3% (+6.6 points) | +| Efficiency | 82.0% — baseline ran, but no comparable score was available; uplift unavailable | 88.5% — baseline ran, but no comparable score was available; uplift unavailable | + +**How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. + +Example: `47.0% → 92.0% (+45.0 points)` means the skill-assisted run scored 92.0%, 45.0 percentage points above its 47.0% no-skill baseline. + +## Token Usage + +Actual Tier 3 execution usage is reported for every observed agent/case pair and both conditions. + +| Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | +|---|---|---:|---:|---:|---:|---| +| claude-code | All cases | 2,361,347 | 6,416,159 | -4,054,812 | -63.20% | skill 3/3; base 3/3 | +| claude-code | codonfm-finetune-001 | 1,038,311 | 3,718,487 | -2,680,176 | -72.08% | skill 1/1; base 1/1 | +| claude-code | codonfm-finetune-002 | 676,297 | 1,890,699 | -1,214,402 | -64.23% | skill 1/1; base 1/1 | +| claude-code | codonfm-finetune-003 | 646,739 | 806,973 | -160,234 | -19.86% | skill 1/1; base 1/1 | +| codex | All cases | 794,045 | 1,840,603 | -1,046,558 | -56.86% | skill 3/3; base 3/3 | +| codex | codonfm-finetune-001 | 351,214 | 721,661 | -370,447 | -51.33% | skill 1/1; base 1/1 | +| codex | codonfm-finetune-002 | 223,229 | 483,694 | -260,465 | -53.85% | skill 1/1; base 1/1 | +| codex | codonfm-finetune-003 | 219,602 | 635,248 | -415,646 | -65.43% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 3,155,392 | 8,256,762 | -5,101,370 | -61.78% | skill 6/6; base 6/6 | + +Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. + +## Tier Status + +| Tier | Purpose | Status | Evidence | +|---|---|---|---| +| Tier 1 | Static validation | **PASSED WITH OBSERVATIONS** | 11 validator(s); 16 finding(s) | +| Tier 2 | Semantic deduplication | **PASSED** | 2 validator(s); 0 finding(s) | +| Tier 3 | Live agent evaluation | **PASS** | 2 agent(s); 3 task(s) | + +## Findings and Observations + +
+Show detailed findings and successful checks + +- **MEDIUM** QUALITY/quality_correctness: No documented scripts in table format (`skills/codonfm-finetune/SKILL.md`) +- **MEDIUM** QUALITY/quality_correctness: Instructions don't mention 'run_script' (`skills/codonfm-finetune/SKILL.md`) +- **MEDIUM** QUALITY/quality_correctness: SKILL_SPEC recommended field missing: 'metadata.tags' (`skills/codonfm-finetune/SKILL.md`) +- **MEDIUM** SECURITY/Unknown (LP3): MCP Least Privilege: The skill declares no explicit tool scope (permissions or allowed-tools) but contains code-execution and file-write capa (`SKILL.md:1`) +- **MEDIUM** SECURITY/Autonomous Decision Making (EA2): Excessive Agency: without checking (`SKILL.md:162`) +- 11 additional finding(s) are available in the full evaluation artifacts. + +
+ +## Scoring Methodology + +
+Show dimension definitions, source signals, and thresholds + +| Dimension | Question | Scored signals | +|---|---|---| +| Security | Is it safe to use? | `security` (100%) | +| Correctness | Is the answer correct? | `accuracy` (100%) | +| Discoverability | Was the right skill loaded when needed? | `skill_execution` (100%) | +| Effectiveness | Did the skill help complete the task? | `goal_accuracy` (50%) + `behavior_check` (50%) | +| Efficiency | Did it avoid wasted tool calls and token usage? | `skill_efficiency` (50%) + `token_efficiency` (50%) | + +- Dimension bands: PASS at 50% or above; NEUTRAL from 40% to below 50%; FAIL below 40%. +- Overall Tier 3 lift: PASS at +5 points or more; FAIL at -10 points or less; values between those bands are NEUTRAL. +- Overall verdict: PASS only when every configured dimension passes for at least one supported agent. Lift is reported as diagnostic evidence and does not override this gate. +- The 50% attempt pass threshold is a separate per-task gate; it is not the dimension pass threshold. +- Effectiveness is the equal-weight mean of goal completion (`goal_accuracy`) and expected workflow adherence (`behavior_check`). +- Efficiency is 50% tool-call productivity (the backward-compatible `skill_efficiency` wire id) and 50% `token_efficiency`. Positive-case skill routing is scored under Discoverability, not Efficiency; a negative case without a routing target is N/A. N/A sources are omitted, remaining weights are renormalized, and the dimension is marked partial. + +Signals present in this run: + +- `security` (Security): unsafe operations, secret leakage, and unauthorized access. +- `skill_execution` (Skill Execution): whether the expected skill was selected, decoys were avoided, and the workflow executed. +- `skill_efficiency` (Tool Productivity): tool-call productivity (legacy wire id; routing is scored under Discoverability). +- `accuracy` (Accuracy): final-answer correctness against the reference answer. +- `goal_accuracy` (Goal Accuracy): whether the user's goal was achieved. +- `behavior_check` (Behavior Check): whether the expected workflow behavior was followed. +- `token_efficiency` (Token Efficiency): actual uncached prompt plus completion usage (50% of Efficiency). + +
+ +## Freshness + +Regenerate this benchmark when the skill, evaluation dataset, target agent/model, evaluator version, environment, or scoring policy changes. diff --git a/skills/codonfm-finetune/skill-card.md b/skills/codonfm-finetune/skill-card.md new file mode 100644 index 0000000..0c788dc --- /dev/null +++ b/skills/codonfm-finetune/skill-card.md @@ -0,0 +1,88 @@ +## Description:
+Fine-tune public CodonFM Encodon checkpoints on labeled coding-sequence or coding-variant data using LoRA, head-only, or full fine-tuning.
+ +This skill is ready for commercial/non-commercial use.
+ +## Owner +NVIDIA
+ +### License/Terms of Use:
+Apache 2.0
+## Use Case:
+Developers and computational biologists fine-tune CodonFM Encodon checkpoints on labeled coding-sequence or variant data for sequence-level regression or classification.
+ +### Deployment Geography for Use:
+Global
+ +## Requirements / Dependencies:
+**Requires API Key or External Credential:** [Not Specified]
+**Credential Type(s):** [None identified]
+ +Do not include secrets in prompts/logs/output; use least-privilege credentials; rotate keys as appropriate.
+ +## Known Risks and Mitigations:
+Risk: Review before execution as proposals could introduce incorrect or misleading guidance into skills.
+Mitigation: Review and scan skill before deployment.
+ +## Reference(s):
+- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
+- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
+- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
+- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
+- [CodonFM Encodon on NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
+- [CenikLab/TE_classic_ML (upstream RiboNN data)](https://github.com/CenikLab/TE_classic_ML/tree/main/data)
+ + +## Skill Output:
+**Output Type(s):** [Shell commands, Configuration instructions, Files]
+**Output Format:** [Markdown with inline bash code blocks]
+**Output Parameters:** [1D]
+**Other Properties Related to Output:** [None]
+ +## Evaluation Agents Used:
+- Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`)
+- Codex (`openai/openai/gpt-5.5`)
+ + + +## Evaluation Tasks:
+Evaluated against 3 internal evaluation tasks (3 positive) across 2 agents, each attempt in an isolated sandbox pod.
+ +## Evaluation Metrics Used:
+Reported benchmark dimensions:
+- Security: Is it safe to use? Checks for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Is the answer correct? Final-answer correctness against the reference answer.
+- Discoverability: Was the right skill loaded when needed? Whether the expected skill was selected, decoys were avoided, and the workflow executed.
+- Effectiveness: Did the skill help complete the task? Equal-weight mean of goal completion and expected workflow adherence.
+- Efficiency: Did it avoid wasted tool calls and token usage? 50% tool-call productivity and 50% token efficiency.
+ +Underlying evaluation signals used in this run:
+- `security`: Unsafe operations, secret leakage, and unauthorized access.
+- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
+- `skill_efficiency`: Tool-call productivity (routing scored under Discoverability).
+- `accuracy`: Final-answer correctness against the reference answer.
+- `goal_accuracy`: Whether the user's goal was achieved.
+- `behavior_check`: Whether the expected workflow behavior was followed.
+- `token_efficiency`: Actual uncached prompt plus completion token usage.
+ + + +## Evaluation Results:
+| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 89.9% | 87.9% | +| Security | 100.0% → 100.0% (±0.0 pts) | 33.3% → 66.7% (+33.4 pts) | +| Correctness | 100.0% → 100.0% (±0.0 pts) | 100.0% → 100.0% (±0.0 pts) | +| Discoverability | 87.7% | 91.0% | +| Effectiveness | 83.3% → 80.0% (-3.3 pts) | 86.7% → 93.3% (+6.6 pts) | +| Efficiency | 82.0% | 88.5% | + +## Skill Version(s):
+29194e8 (source: git SHA, committed 2026-09-18)
+ +## Ethical Considerations:
+NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
+ +(For Release on NVIDIA Platforms Only)
+Please report quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://app.intigriti.com/programs/nvidia/nvidiavdp/detail).
diff --git a/skills/codonfm-finetune/skill.oms.sig b/skills/codonfm-finetune/skill.oms.sig new file mode 100644 index 0000000..e7afea2 --- /dev/null +++ b/skills/codonfm-finetune/skill.oms.sig @@ -0,0 +1 @@ +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1maW5ldHVuZSIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI3ZjBmN2ZmYjZmMGE0MGY2ZTU0YzcwNTgxMmRlZTE1NTU5ZmM5MmZkNTU3MjEyYTRkMGEzNDUwNTI4ODk5M2IwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAic2VyaWFsaXphdGlvbiI6IHsKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UsCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIsCiAgICAgICJpZ25vcmVfcGF0aHMiOiBbCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0aHViIgogICAgICBdCiAgICB9LAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjcyMzc2NDI0ZTc4ZTJmZDZiMmJkYzA0ZjA0ZmMyMWVkMzViYTQ2YzAzM2NkODc3NDUzZmMwMWQ2NThiOGFjZWQiLAogICAgICAgICJuYW1lIjogIkJFTkNITUFSSy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjViNTVjZDkxNjRjYzVkYzI3ZTk5NjViNzg1YmM4MjdmYzdlOTQ1ZjgwZDMxZTJhMjZhNmMyN2FiYTFmYTQ2YTMiLAogICAgICAgICJuYW1lIjogIlNLSUxMLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzllY2Q5OTgyMTdjNWE3NWE5YTFlZjZiZTE3MGJkMGEzYjY2ODQ1NGM3MWQ3Y2NjZGVjYTUxYmQyYWM5MjJkYyIsCiAgICAgICAgIm5hbWUiOiAiYWdlbnRzL29wZW5haS55YW1sIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjEwYWEzOWEyNzI4OGViYzhiNTIwNTY2NjVmMGZlZmEwZTk4MmQwOWY1OThlYzUwMjAyNTM4ODE3ZDhlYmIwYiIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImYwYzEyMmYxOWEyZDMxNmZlOTVjY2ZhYTBhYzUxYmQ2MTNjZmFiZGQwOWZkODlmYmI3NjY1NjJhNWYwZWVkMjciLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2NvZG9uZm1fc291cmNlLnppcCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2VuY29kb25fY2hlY2twb2ludC5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNGMyMzUxNzk0MGIwOGYxODk2ZjMyY2FmNmYwNGU2MDMwNGQzNDNlYmY2M2YzZWZjYTY3ZjJiZjhjNTBjZjc2ZCIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvcmlib25uX3Ntb2tlLnByb3ZlbmFuY2UuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImYzODVhMTgxYTM3YzdhZDU4Y2U0N2VmNWY2OTYyMDk2ZGNiODMwNjMxMjkwYmM4NDdhNjdiODY4OTZmN2FmN2UiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL3JpYm9ubl9zbW9rZS50c3YiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmOTA0NDAwMGQ2ODFiMjVlNjdiYWI4OGJkZDk0YTUyNGI0ODQ3MDY1YjI4YzhlZDcwY2ExYTYyMWI3OWFkZWIzIiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy92YXJpYW50c19sYWJlbGVkLmNzdiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViMmVhYzI4ZTc3YzRkZWMwNzdiZTgwNjk2MGFlOGJiMzI5ZDg2ODYzM2IyMWYyYzQ5MWE2NTU1YmI1NjU3MTEiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL3ZhcmlhbnRzX2xhYmVsZWQucHJvdmVuYW5jZS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOGYwZTA0NjBlYzc1OGNjMGQzOGIzYWExMGIzZDFjYzZmNWRlNzc1ODI3NjUxMTg3NWNkMjU1MDU0ODM1ZDk1YyIsCiAgICAgICAgIm5hbWUiOiAic2NyaXB0cy9wcmVwYXJlX3JpYm9ubi5weSIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNjMjQ5Y2RkMmMzNmVhYTAzZGFkYzFkMTg0NzJiMjViZDY4MzU5MmEwNjUzZDJkNDZmMDcxOWVkYjk4YjkwOTciLAogICAgICAgICJuYW1lIjogInNraWxsLWNhcmQubWQiCiAgICAgIH0KICAgIF0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGQCMHo1dOSS6vYDVPddO5PvaSt8OgLxOPkl3M9Hsx3jQL4cd5RHEuepPMXjoXdq3k3g2QIwHlRlfue3DBNYjPgR5X9aZdfIQACrQRW0/JrnrP2gs9YZwIyQRM1bNRTrDTT74v42","keyid":""}]}} \ No newline at end of file diff --git a/skills/codonfm-score/BENCHMARK.md b/skills/codonfm-score/BENCHMARK.md new file mode 100644 index 0000000..d49877a --- /dev/null +++ b/skills/codonfm-score/BENCHMARK.md @@ -0,0 +1,125 @@ +# Skill Benchmark: codonfm-score + +> ✅ **Overall verdict: PASS — Recommended for publication** + +## Publication Recommendation + +Recommended for publication based on the completed evaluation evidence in this report. + +## Evaluation Metadata + +- Skill: `codonfm-score` +- Evaluation date: 2026-10-02 +- Evaluator version: `1.5.6` +- Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-5`), Codex (`openai/openai/gpt-5.5`) +- Tasks: 3 evaluation tasks (2 positive, 1 negative) +- Dataset digest: `sha256:5473a6b7ef522b019ec4b46023830ce8ac22da3576b0c97bf10acb2ac28e9d79` (skill-evaluator-dataset-snapshot/1) +- Attempts per task: 1 +- Environment: `k8s-sandbox` +- Tier 2 evidence: required for publication +- Tier 3 evidence: required for publication + +Each task attempt ran in its own isolated sandbox pod. + +## What This Report Answers + +The three-tier evaluation checks whether the skill: + +- is safe to use; +- produces correct answers; +- is discovered and activated when needed; +- helps the agent complete the user's goal and expected workflow; and +- avoids wasted skill and tool usage. + +## Results at a Glance + +| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 88.8% — baseline ran, but no comparable score was available; uplift unavailable | 91.6% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 100.0% → 66.7% (-33.3 points) | 66.7% → 100.0% (+33.3 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 97.5% — baseline ran, but no comparable score was available; uplift unavailable | 75.0% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 96.7% → 90.0% (-6.7 points) | 93.3% → 93.3% (±0.0 points) | +| Efficiency | 90.0% — baseline ran, but no comparable score was available; uplift unavailable | 89.6% — baseline ran, but no comparable score was available; uplift unavailable | + +**How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. + +Example: `47.0% → 92.0% (+45.0 points)` means the skill-assisted run scored 92.0%, 45.0 percentage points above its 47.0% no-skill baseline. + +A partial dimension was calculated from only the available configured signals; review the detailed report before relying on it. + +## Token Usage + +Actual Tier 3 execution usage is reported for every observed agent/case pair and both conditions. + +| Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | +|---|---|---:|---:|---:|---:|---| +| claude-code | All cases | 1,540,091 | 2,144,581 | -604,490 | -28.19% | skill 3/3; base 3/3 | +| claude-code | codonfm-score-001 | 713,780 | 1,298,851 | -585,071 | -45.05% | skill 1/1; base 1/1 | +| claude-code | codonfm-score-002 | 672,716 | 726,625 | -53,909 | -7.42% | skill 1/1; base 1/1 | +| claude-code | codonfm-score-003 | 153,595 | 119,105 | +34,490 | +28.96% | skill 1/1; base 1/1 | +| codex | All cases | 921,033 | 1,390,032 | -468,999 | -33.74% | skill 3/3; base 3/3 | +| codex | codonfm-score-001 | 390,238 | 816,264 | -426,026 | -52.19% | skill 1/1; base 1/1 | +| codex | codonfm-score-002 | 517,306 | 518,092 | -786 | -0.15% | skill 1/1; base 1/1 | +| codex | codonfm-score-003 | 13,489 | 55,676 | -42,187 | -75.77% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 2,461,124 | 3,534,613 | -1,073,489 | -30.37% | skill 6/6; base 6/6 | + +Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. + +## Tier Status + +| Tier | Purpose | Status | Evidence | +|---|---|---|---| +| Tier 1 | Static validation | **PASSED WITH OBSERVATIONS** | 11 validator(s); 7 finding(s) | +| Tier 2 | Semantic deduplication | **PASSED** | 2 validator(s); 0 finding(s) | +| Tier 3 | Live agent evaluation | **PASS** | 2 agent(s); 3 task(s) | + +## Findings and Observations + +
+Show detailed findings and successful checks + +- **MEDIUM** QUALITY/quality_correctness: SKILL_SPEC recommended field missing: 'metadata.tags' (`skills/codonfm-score/SKILL.md`) +- **LOW** QUALITY/quality_discoverability: Description very long (385 chars, recommend 50-150) (`skills/codonfm-score/SKILL.md`) +- **LOW** QUALITY/quality_discoverability: No '## Purpose' section (`skills/codonfm-score/SKILL.md`) +- **LOW** QUALITY/quality_reliability: No prerequisites/requirements documented (`skills/codonfm-score/SKILL.md`) +- **LOW** QUALITY/quality_reliability: No limitations documented (`skills/codonfm-score/SKILL.md`) +- 2 additional finding(s) are available in the full evaluation artifacts. + +
+ +## Scoring Methodology + +
+Show dimension definitions, source signals, and thresholds + +| Dimension | Question | Scored signals | +|---|---|---| +| Security | Is it safe to use? | `security` (100%) | +| Correctness | Is the answer correct? | `accuracy` (100%) | +| Discoverability | Was the right skill loaded when needed? | `skill_execution` (100%) | +| Effectiveness | Did the skill help complete the task? | `goal_accuracy` (50%) + `behavior_check` (50%) | +| Efficiency | Did it avoid wasted tool calls and token usage? | `skill_efficiency` (50%) + `token_efficiency` (50%) | + +- Dimension bands: PASS at 50% or above; NEUTRAL from 40% to below 50%; FAIL below 40%. +- Overall Tier 3 lift: PASS at +5 points or more; FAIL at -10 points or less; values between those bands are NEUTRAL. +- Overall verdict: PASS only when every configured dimension passes for at least one supported agent. Lift is reported as diagnostic evidence and does not override this gate. +- The 50% attempt pass threshold is a separate per-task gate; it is not the dimension pass threshold. +- Effectiveness is the equal-weight mean of goal completion (`goal_accuracy`) and expected workflow adherence (`behavior_check`). +- Efficiency is 50% tool-call productivity (the backward-compatible `skill_efficiency` wire id) and 50% `token_efficiency`. Positive-case skill routing is scored under Discoverability, not Efficiency; a negative case without a routing target is N/A. N/A sources are omitted, remaining weights are renormalized, and the dimension is marked partial. + +Signals present in this run: + +- `security` (Security): unsafe operations, secret leakage, and unauthorized access. +- `skill_execution` (Skill Execution): whether the expected skill was selected, decoys were avoided, and the workflow executed. +- `skill_efficiency` (Tool Productivity): tool-call productivity (legacy wire id; routing is scored under Discoverability). +- `accuracy` (Accuracy): final-answer correctness against the reference answer. +- `goal_accuracy` (Goal Accuracy): whether the user's goal was achieved. +- `behavior_check` (Behavior Check): whether the expected workflow behavior was followed. +- `token_efficiency` (Token Efficiency): actual uncached prompt plus completion usage (50% of Efficiency). + +
+ +## Freshness + +Regenerate this benchmark when the skill, evaluation dataset, target agent/model, evaluator version, environment, or scoring policy changes. diff --git a/skills/codonfm-score/skill-card.md b/skills/codonfm-score/skill-card.md new file mode 100644 index 0000000..c438315 --- /dev/null +++ b/skills/codonfm-score/skill-card.md @@ -0,0 +1,87 @@ +## Description:
+Validate, prepare, or run public CodonFM Encodon masked-codon variant scoring and review compatibility of its scoring workflows.
+ +This skill is ready for commercial/non-commercial use.
+ +## Owner +NVIDIA
+ +### License/Terms of Use:
+Apache 2.0
+## Use Case:
+Developers and bioinformatics researchers use this skill to validate, prepare, and execute masked-codon variant scoring with public CodonFM Encodon models for codon-level genomic analysis.
+ +### Deployment Geography for Use:
+Global
+ +## Requirements / Dependencies:
+**Requires API Key or External Credential:** [Not Specified]
+**Credential Type(s):** [None identified]
+ +Do not include secrets in prompts/logs/output; use least-privilege credentials; rotate keys as appropriate.
+ +## Known Risks and Mitigations:
+Risk: Review before execution as proposals could introduce incorrect or misleading guidance into skills.
+Mitigation: Review and scan skill before deployment.
+ +## Reference(s):
+- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
+- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
+- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
+- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
+- [CodonFM Encodon (NGC Catalog)](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NVIDIA Drug and Biomolecular Research](https://research.nvidia.com/labs/dbr)
+ + +## Skill Output:
+**Output Type(s):** [Shell commands, Analysis, Configuration instructions]
+**Output Format:** [Markdown with inline bash code blocks]
+**Output Parameters:** [1D]
+**Other Properties Related to Output:** [None]
+ +## Evaluation Agents Used:
+- Claude Code (`aws/anthropic/bedrock-claude-opus-5`)
+- Codex (`openai/openai/gpt-5.5`)
+ + + +## Evaluation Tasks:
+3 evaluation tasks (2 positive, 1 negative) run in isolated sandbox pods, evaluator version 1.5.6.
+ +## Evaluation Metrics Used:
+Reported benchmark dimensions:
+- Security: Checks for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Final-answer correctness against the reference answer.
+- Discoverability: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
+- Effectiveness: Equal-weight mean of goal completion (goal_accuracy) and expected workflow adherence (behavior_check).
+- Efficiency: 50% tool-call productivity and 50% token efficiency.
+ +Underlying evaluation signals used in this run:
+- `security`: Unsafe operations, secret leakage, and unauthorized access.
+- `accuracy`: Final-answer correctness against the reference answer.
+- `skill_execution`: Whether the expected skill was selected and the workflow executed.
+- `goal_accuracy`: Whether the user's goal was achieved.
+- `behavior_check`: Whether the expected workflow behavior was followed.
+- `skill_efficiency`: Tool-call productivity (routing scored under Discoverability).
+- `token_efficiency`: Actual uncached prompt plus completion token usage.
+ + + +## Evaluation Results:
+| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 88.8% | 91.6% | +| Security | 100.0% → 66.7% (-33.3 points) | 66.7% → 100.0% (+33.3 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 97.5% | 75.0% | +| Effectiveness | 96.7% → 90.0% (-6.7 points) | 93.3% → 93.3% (±0.0 points) | +| Efficiency | 90.0% | 89.6% | + +## Skill Version(s):
+29194e8 (source: git SHA, committed 2026-09-18)
+ +## Ethical Considerations:
+NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
+ +(For Release on NVIDIA Platforms Only)
+Please report quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://app.intigriti.com/programs/nvidia/nvidiavdp/detail).
diff --git a/skills/codonfm-score/skill.oms.sig b/skills/codonfm-score/skill.oms.sig new file mode 100644 index 0000000..24928d7 --- /dev/null +++ b/skills/codonfm-score/skill.oms.sig @@ -0,0 +1 @@ +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1zY29yZSIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIxNjM5MDg3YzFjZTQyYjAxYzk0YmJjZTVlM2M4YTg3YWY4MGM4OWI3Yjg1ZjU0MDIxMDU5Njg1ODk1YmJkNzEzIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICI2M2QwZTEzMWE0ODQ2YjRlMGM2NzhmNTIyOTZkYzgyNzlmZDkyZjIyMDAzMTQwNWVhNWQwNTc1NTU5NTc4OTJhIiwKICAgICAgICAibmFtZSI6ICJCRU5DSE1BUksubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICIxOGY3NjYyYTUzYTg2YWM1ZmM2OTUyNjFlODFkMTViOTA0ZmUzMmQ1NzQwZGM1N2IyZWVmZmQ3MWM0MGEzNTZmIiwKICAgICAgICAibmFtZSI6ICJTS0lMTC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogImFjM2JjNWU1YzdjMzhjMjg0MjczYzY4MTgzNDI2YWE0OWY5ZjMzZmVkOGQzYTU4ZTNjZjIxYmFiOGZiZjUyYTYiLAogICAgICAgICJuYW1lIjogImFnZW50cy9vcGVuYWkueWFtbCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogImQwMGFhYmMzNTQ2MzZiODk2NzI5NWM3ZmM4YTk5ZjJlMGIzMjMyODViNzdlODRlNjU2YjQ5ODBhNzBlODUyNjUiLAogICAgICAgICJuYW1lIjogImV2YWxzL2NvbmZpZy55bWwiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICJkOWU4N2FmZmIyMTZmZjg3YjlmNGIwMTkxYTcwOWUyMDM5NzJmMWY5ZTk0NTZkYmRkYTNmNjE1MWYyMWZjMTM3IiwKICAgICAgICAibmFtZSI6ICJldmFscy9ldmFscy5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiZjBjMTIyZjE5YTJkMzE2ZmU5NWNjZmFhMGFjNTFiZDYxM2NmYWJkZDA5ZmQ4OWZiYjc2NjU2MmE1ZjBlZWQyNyIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvY29kb25mbV9zb3VyY2UuemlwIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiNjk4ZmEzNTQxNDAwMzNlOWFhODU5MWNkMjg2MDcwM2E0ZmNkYzJlYjI3ZWRjOGYyZjA3NjBjNDA4M2U1NjNlZSIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvZW5jb2Rvbl9jaGVja3BvaW50Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICJlMjhiNjMzMGRiMDA3NTkxZWNkZGUyMTU1YTJmMTE4ZjQxZTIzNjk5NzNiNWI4MGQ1NmE2OTg0NmEyMGQ1NjBkIiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy92YXJpYW50cy5jc3YiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICI0YTUzNzk5NDY4MjExNTczYWU2YWI4ZWI4NTU4ZWQ2OGM2OGFkOTM0ZDkxM2I0MThhMmE4YWZlZGNkMjBiNDhmIiwKICAgICAgICAibmFtZSI6ICJza2lsbC1jYXJkLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAiaGFzaF90eXBlIjogInNoYTI1NiIsCiAgICAgICJpZ25vcmVfcGF0aHMiOiBbCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0aHViIgogICAgICBdLAogICAgICAibWV0aG9kIjogImZpbGVzIiwKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGYCMQCxOlmeztfHY/2TBXcn849LYs9Lq7uX5XKoFU34SjdY3SFvi0J55R1nWl2DNstVjlACMQCYzrVGNxO/i2Bp2EoQ58CGGDnNCoMn3tv+ooe9cVb32vQ6aM3Df4xK8ZdhgFCqm7Q=","keyid":""}]}} \ No newline at end of file diff --git a/skills/codonfm-setup/BENCHMARK.md b/skills/codonfm-setup/BENCHMARK.md new file mode 100644 index 0000000..d8f2f45 --- /dev/null +++ b/skills/codonfm-setup/BENCHMARK.md @@ -0,0 +1,123 @@ +# Skill Benchmark: codonfm-setup + +> ✅ **Overall verdict: PASS — Recommended for publication** + +## Publication Recommendation + +Recommended for publication based on the completed evaluation evidence in this report. + +## Evaluation Metadata + +- Skill: `codonfm-setup` +- Evaluation date: 2026-10-02 +- Evaluator version: `1.5.6` +- Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-5`), Codex (`openai/openai/gpt-5.5`) +- Tasks: 3 evaluation tasks (3 positive) +- Dataset digest: `sha256:da7c67cf041d32b7e9920ac29786e8d039bfe948533853dbb0bbf38c553dc8d9` (skill-evaluator-dataset-snapshot/1) +- Attempts per task: 1 +- Environment: `k8s-sandbox` +- Tier 2 evidence: required for publication +- Tier 3 evidence: required for publication + +Each task attempt ran in its own isolated sandbox pod. + +## What This Report Answers + +The three-tier evaluation checks whether the skill: + +- is safe to use; +- produces correct answers; +- is discovered and activated when needed; +- helps the agent complete the user's goal and expected workflow; and +- avoids wasted skill and tool usage. + +## Results at a Glance + +| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 93.6% — baseline ran, but no comparable score was available; uplift unavailable | 95.7% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 66.7% → 100.0% (+33.3 points) | 66.7% → 100.0% (+33.3 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 95.0% — baseline ran, but no comparable score was available; uplift unavailable | 90.0% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 83.3% → 87.5% (+4.2 points) | 85.8% → 95.0% (+9.2 points) | +| Efficiency | 85.6% — baseline ran, but no comparable score was available; uplift unavailable | 93.5% — baseline ran, but no comparable score was available; uplift unavailable | + +**How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. + +Example: `47.0% → 92.0% (+45.0 points)` means the skill-assisted run scored 92.0%, 45.0 percentage points above its 47.0% no-skill baseline. + +## Token Usage + +Actual Tier 3 execution usage is reported for every observed agent/case pair and both conditions. + +| Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | +|---|---|---:|---:|---:|---:|---| +| claude-code | All cases | 2,462,441 | 5,734,843 | -3,272,402 | -57.06% | skill 3/3; base 3/3 | +| claude-code | codonfm-setup-001 | 393,909 | 1,408,765 | -1,014,856 | -72.04% | skill 1/1; base 1/1 | +| claude-code | codonfm-setup-002 | 537,459 | 672,118 | -134,659 | -20.04% | skill 1/1; base 1/1 | +| claude-code | codonfm-setup-003 | 1,531,073 | 3,653,960 | -2,122,887 | -58.10% | skill 1/1; base 1/1 | +| codex | All cases | 676,905 | 1,297,601 | -620,696 | -47.83% | skill 3/3; base 3/3 | +| codex | codonfm-setup-001 | 161,662 | 220,831 | -59,169 | -26.79% | skill 1/1; base 1/1 | +| codex | codonfm-setup-002 | 339,512 | 563,013 | -223,501 | -39.70% | skill 1/1; base 1/1 | +| codex | codonfm-setup-003 | 175,731 | 513,757 | -338,026 | -65.79% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 3,139,346 | 7,032,444 | -3,893,098 | -55.36% | skill 6/6; base 6/6 | + +Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. + +## Tier Status + +| Tier | Purpose | Status | Evidence | +|---|---|---|---| +| Tier 1 | Static validation | **PASSED WITH OBSERVATIONS** | 11 validator(s); 8 finding(s) | +| Tier 2 | Semantic deduplication | **PASSED** | 2 validator(s); 0 finding(s) | +| Tier 3 | Live agent evaluation | **PASS** | 2 agent(s); 3 task(s) | + +## Findings and Observations + +
+Show detailed findings and successful checks + +- **MEDIUM** QUALITY/quality_correctness: SKILL_SPEC recommended field missing: 'metadata.tags' (`skills/codonfm-setup/SKILL.md`) +- **MEDIUM** SCHEMA/body_recommended_section: Missing recommended section: '## Examples' (`skills/codonfm-setup/SKILL.md`) +- **LOW** QUALITY/quality_discoverability: Description very long (426 chars, recommend 50-150) (`skills/codonfm-setup/SKILL.md`) +- **LOW** QUALITY/quality_discoverability: No '## Purpose' section (`skills/codonfm-setup/SKILL.md`) +- **LOW** QUALITY/quality_reliability: No prerequisites/requirements documented (`skills/codonfm-setup/SKILL.md`) +- 3 additional finding(s) are available in the full evaluation artifacts. + +
+ +## Scoring Methodology + +
+Show dimension definitions, source signals, and thresholds + +| Dimension | Question | Scored signals | +|---|---|---| +| Security | Is it safe to use? | `security` (100%) | +| Correctness | Is the answer correct? | `accuracy` (100%) | +| Discoverability | Was the right skill loaded when needed? | `skill_execution` (100%) | +| Effectiveness | Did the skill help complete the task? | `goal_accuracy` (50%) + `behavior_check` (50%) | +| Efficiency | Did it avoid wasted tool calls and token usage? | `skill_efficiency` (50%) + `token_efficiency` (50%) | + +- Dimension bands: PASS at 50% or above; NEUTRAL from 40% to below 50%; FAIL below 40%. +- Overall Tier 3 lift: PASS at +5 points or more; FAIL at -10 points or less; values between those bands are NEUTRAL. +- Overall verdict: PASS only when every configured dimension passes for at least one supported agent. Lift is reported as diagnostic evidence and does not override this gate. +- The 50% attempt pass threshold is a separate per-task gate; it is not the dimension pass threshold. +- Effectiveness is the equal-weight mean of goal completion (`goal_accuracy`) and expected workflow adherence (`behavior_check`). +- Efficiency is 50% tool-call productivity (the backward-compatible `skill_efficiency` wire id) and 50% `token_efficiency`. Positive-case skill routing is scored under Discoverability, not Efficiency; a negative case without a routing target is N/A. N/A sources are omitted, remaining weights are renormalized, and the dimension is marked partial. + +Signals present in this run: + +- `security` (Security): unsafe operations, secret leakage, and unauthorized access. +- `skill_execution` (Skill Execution): whether the expected skill was selected, decoys were avoided, and the workflow executed. +- `skill_efficiency` (Tool Productivity): tool-call productivity (legacy wire id; routing is scored under Discoverability). +- `accuracy` (Accuracy): final-answer correctness against the reference answer. +- `goal_accuracy` (Goal Accuracy): whether the user's goal was achieved. +- `behavior_check` (Behavior Check): whether the expected workflow behavior was followed. +- `token_efficiency` (Token Efficiency): actual uncached prompt plus completion usage (50% of Efficiency). + +
+ +## Freshness + +Regenerate this benchmark when the skill, evaluation dataset, target agent/model, evaluator version, environment, or scoring policy changes. diff --git a/skills/codonfm-setup/skill-card.md b/skills/codonfm-setup/skill-card.md new file mode 100644 index 0000000..2ccaa13 --- /dev/null +++ b/skills/codonfm-setup/skill-card.md @@ -0,0 +1,88 @@ +## Description:
+Set up the public CodonFM v1 repository and download public Encodon checkpoints.
+ +This skill is ready for commercial/non-commercial use.
+ +## Owner +NVIDIA
+ +### License/Terms of Use:
+Apache 2.0
+## Use Case:
+Developers and engineers setting up the CodonFM development environment, building or launching the development container, configuring data and checkpoint mounts, verifying GPU access, and downloading public Encodon model weights.
+ +### Deployment Geography for Use:
+Global
+ +## Requirements / Dependencies:
+**Requires API Key or External Credential:** [No]
+**Credential Type(s):** [None]
+ +Do not include secrets in prompts/logs/output; use least-privilege credentials; rotate keys as appropriate.
+ +## Known Risks and Mitigations:
+Risk: Review before execution as proposals could introduce incorrect or misleading guidance into skills.
+Mitigation: Review and scan skill before deployment.
+ +## Reference(s):
+- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
+- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
+- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
+- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
+- [NV CodonFM Encodon (NGC Catalog)](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NVIDIA CUDA Compatibility](https://docs.nvidia.com/deploy/cuda-compatibility/minor-version-compatibility.html)
+- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
+ + +## Skill Output:
+**Output Type(s):** [Shell commands, Configuration instructions]
+**Output Format:** [Markdown with inline bash code blocks]
+**Output Parameters:** [1D]
+**Other Properties Related to Output:** [None]
+ +## Evaluation Agents Used:
+- Claude Code (`aws/anthropic/bedrock-claude-opus-5`)
+- Codex (`openai/openai/gpt-5.5`)
+ + + +## Evaluation Tasks:
+Evaluated against 3 internal evaluation tasks (3 positive) in isolated k8s-sandbox pods with 1 attempt per task.
+ +## Evaluation Metrics Used:
+Reported benchmark dimensions:
+- Security: Checks for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Checks final-answer correctness against the reference answer.
+- Discoverability: Checks whether the expected skill was selected, decoys were avoided, and the workflow executed.
+- Effectiveness: Checks whether the user's goal was achieved and expected workflow behavior was followed.
+- Efficiency: Checks tool-call productivity and token efficiency.
+ +Underlying evaluation signals used in this run:
+- `security`: Unsafe operations, secret leakage, and unauthorized access.
+- `accuracy`: Final-answer correctness against the reference answer.
+- `skill_execution`: Whether the expected skill was selected and the workflow executed.
+- `goal_accuracy`: Whether the user's goal was achieved.
+- `behavior_check`: Whether the expected workflow behavior was followed.
+- `skill_efficiency`: Tool-call productivity.
+- `token_efficiency`: Actual uncached prompt plus completion token usage.
+ + + +## Evaluation Results:
+| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +|---|---:|---:| +| Overall | 93.6% | 95.7% | +| Security | 66.7% → 100.0% (+33.3 pts) | 66.7% → 100.0% (+33.3 pts) | +| Correctness | 100.0% → 100.0% (±0.0 pts) | 100.0% → 100.0% (±0.0 pts) | +| Discoverability | 95.0% | 90.0% | +| Effectiveness | 83.3% → 87.5% (+4.2 pts) | 85.8% → 95.0% (+9.2 pts) | +| Efficiency | 85.6% | 93.5% | + +## Skill Version(s):
+29194e8 (source: git SHA, committed 2026-09-18)
+ +## Ethical Considerations:
+NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
+ +(For Release on NVIDIA Platforms Only)
+Please report quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://app.intigriti.com/programs/nvidia/nvidiavdp/detail).
diff --git a/skills/codonfm-setup/skill.oms.sig b/skills/codonfm-setup/skill.oms.sig new file mode 100644 index 0000000..29d33d8 --- /dev/null +++ b/skills/codonfm-setup/skill.oms.sig @@ -0,0 +1 @@ +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1zZXR1cCIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI0ZTczMGJkOTk2ZWJjYTJiOGI3ZTZmZWU4OWY3Y2M4MWMzM2Q2ODMxZDVlN2IyMjFkMThjZDM0YmM4YzdhZDhmIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIiwKICAgICAgICAiZGlnZXN0IjogImY4ZDA4ZjZhZTVhODIxNzhlYjFkM2I1NWFkYjcwY2QwZDE5ZjYyYzE0NGE3NmUzNDEzOTZhMWY2MTBjNTU2MGMiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiLAogICAgICAgICJkaWdlc3QiOiAiM2ZlZmNiOWRkMmM3ODk3YzQzYTkwMjllZWEwM2ViM2FjMWVlZWI5MGZjMDhhOTNlNDQ1ODYwOWUxOGY1OTcyYiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiLAogICAgICAgICJkaWdlc3QiOiAiY2I3ZmZkYWI5Yjk2OWQxNWE3OWU3ODk1MWRmNDEzM2NlNzQzMGI4ZTBjZTgwMjNkZThhMjE0MDY2YzQxNmE1ZiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9jb25maWcueW1sIiwKICAgICAgICAiZGlnZXN0IjogImQwMGFhYmMzNTQ2MzZiODk2NzI5NWM3ZmM4YTk5ZjJlMGIzMjMyODViNzdlODRlNjU2YjQ5ODBhNzBlODUyNjUiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIsCiAgICAgICAgImRpZ2VzdCI6ICIxYTA5Y2Y3ZWJhMmY2ZjcyZTg3NzdhNzkzYmRkYWNlMGNhNWJhODFmZmJhNDcxZGM0MmE3ZjJkMGE0Yzg5OGZjIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2NvZG9uZm1fc291cmNlLnppcCIsCiAgICAgICAgImRpZ2VzdCI6ICJmMGMxMjJmMTlhMmQzMTZmZTk1Y2NmYWEwYWM1MWJkNjEzY2ZhYmRkMDlmZDg5ZmJiNzY2NTYyYTVmMGVlZDI3IiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2VuY29kb25fY2hlY2twb2ludC5qc29uIiwKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIsCiAgICAgICAgImRpZ2VzdCI6ICJlNGNjNWEwNzM0NTFmYTEwMjNlNzQyZDM0Y2U1YjE0Y2U1YjNhNDk2Yzc5ODRlMDdlNDY1ZGI0N2ZiMDkxNmJmIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAiYWxsb3dfc3ltbGlua3MiOiBmYWxzZSwKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGlnbm9yZSIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXRodWIiCiAgICAgIF0sCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGYCMQDEyDQIMbFnBevznX9NvtabpfzLcch7B2WYR6+fkeD6sVOAKXp+v1rLxhmW7g1lcWACMQDUINCJ3uR+dTt+WICoRJ8qwWPWKNgHPuoR06fnbhgyA+LBCLRPAPu/06RM4Yg78p0=","keyid":""}]}} \ No newline at end of file From 4561fadb8421c247f97d3fddd8f923bfd3843486 Mon Sep 17 00:00:00 2001 From: xinf Date: Fri, 2 Oct 2026 15:56:16 -0700 Subject: [PATCH 11/13] Add two harder codonfm-embed eval cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - codonfm-embed-003: malformed sequences_edge_cases.csv (blank split, duplicate id, oversized/truncated sequence) — tests whether the agent catches known footguns the skill documents explicitly, rather than just validating a clean CSV. - codonfm-embed-004: checkpoint-choice question that requires the preprint's benchmark knowledge rather than anything derivable from the supplied source code alone — tests the skill's distilled domain knowledge, not just code-reading ability. Both target gaps identified while investigating why codonfm-embed showed flat/negative lift in Tier 3 runs: the existing two cases are fully answerable from the shipped source code alone, so a careful baseline can match a skill-equipped agent on content. These add cases where skill content should matter more than brute-force source reading. Co-Authored-By: Claude Sonnet 5 --- skills/codonfm-embed/evals/evals.json | 35 +++++++++++++++++++ .../evals/files/sequences_edge_cases.csv | 5 +++ 2 files changed, 40 insertions(+) create mode 100644 skills/codonfm-embed/evals/files/sequences_edge_cases.csv diff --git a/skills/codonfm-embed/evals/evals.json b/skills/codonfm-embed/evals/evals.json index e155005..dda3586 100644 --- a/skills/codonfm-embed/evals/evals.json +++ b/skills/codonfm-embed/evals/evals.json @@ -33,6 +33,41 @@ ], "expected_skill": "codonfm-embed", "expected_script": null + }, + { + "id": "codonfm-embed-003", + "prompt": "Validate sequences_edge_cases.csv before running Encodon embedding extraction. Some rows look unusual — confirm whether each will process successfully, and if not, say exactly what happens and how to fix it.", + "files": [ + "files/codonfm_source.zip", + "files/encodon_checkpoint.json", + "files/sequences_edge_cases.csv" + ], + "expected_output": "A per-row verdict: row 1 (row_normal) processes normally; row 2 (row_blank_split) is silently excluded from the test split, not an error, and should be set to split=test to be included; row 3 shares its id with row 1, which the agent should flag as a duplicate that will make ids_merged.npy ambiguous; row 4 (row_oversized, 2050 codons) exceeds the context-length codon limit and will be truncated rather than embedded in full.", + "assertions": [ + "The agent correctly identifies that the row with a blank split value will be silently excluded from extraction because the eval path filters strictly on split=='test', not treated as an error or crash", + "The agent flags the duplicate id across two rows and explains the consequence for mapping ids_merged.npy back to source rows", + "The agent identifies that the oversized sequence exceeds context_length - 2 codons and will be truncated rather than rejected, and explains what information is lost", + "The agent does not claim all four rows will process identically, and does not fabricate output values" + ], + "expected_skill": "codonfm-embed", + "expected_script": null + }, + { + "id": "codonfm-embed-004", + "prompt": "We want the most reliable embeddings for training a translation-efficiency regressor on a small (~200-sequence) labeled set. Which public Encodon checkpoint should we use, and what correlation with ground truth should we expect? Justify the choice.", + "files": [ + "files/codonfm_source.zip", + "files/encodon_checkpoint.json" + ], + "expected_output": "A recommendation for the largest available public checkpoint (1B, or 1B-Cdwt for codon-frequency-weighted masking), citing the CodonFM preprint's reported translation-efficiency correlation advantage over smaller checkpoints, with an explicit caveat that a published benchmark correlation is not a performance guarantee on this specific 200-sequence dataset.", + "assertions": [ + "The agent recommends a specific public checkpoint size rather than declining to answer", + "The agent cites the preprint/published benchmarks as the source of its performance claim, not the supplied source code (which contains no benchmark numbers at all)", + "The agent distinguishes the published benchmark result from a guarantee on the user's own small labeled set", + "The agent does not claim access to internal-only benchmark numbers beyond what the public preprint documents" + ], + "expected_skill": "codonfm-embed", + "expected_script": null } ] } diff --git a/skills/codonfm-embed/evals/files/sequences_edge_cases.csv b/skills/codonfm-embed/evals/files/sequences_edge_cases.csv new file mode 100644 index 0000000..8c5554d --- /dev/null +++ b/skills/codonfm-embed/evals/files/sequences_edge_cases.csv @@ -0,0 +1,5 @@ +id,ref_seq,value,split +row_normal,ATGGCTGAATTTCCGTAA,0.0,test +row_blank_split,ATGGCAGAATTTCCGTAA,0.0, +row_normal,ATGGCCGAATTTCCGTAA,0.0,test +row_oversized,ATGGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTGCTTAA,0.0,test From be431178ee32ca749c8aec06045df26779bc1fa1 Mon Sep 17 00:00:00 2001 From: Ohad Mosafi Date: Wed, 7 Oct 2026 10:36:38 -0700 Subject: [PATCH 12/13] Improve codonfm-embed checkpoint guidance and input validation Signed-off-by: Ohad Mosafi --- skills/codonfm-embed/SKILL.md | 200 ++++++++++++------ .../references/checkpoint-selection.md | 42 ++++ .../codonfm-embed/scripts/validate_inputs.py | 126 +++++++++++ .../tests/test_validate_inputs.py | 121 +++++++++++ 4 files changed, 429 insertions(+), 60 deletions(-) create mode 100644 skills/codonfm-embed/references/checkpoint-selection.md create mode 100644 skills/codonfm-embed/scripts/validate_inputs.py create mode 100644 skills/codonfm-embed/tests/test_validate_inputs.py diff --git a/skills/codonfm-embed/SKILL.md b/skills/codonfm-embed/SKILL.md index 2e72c40..10e0035 100644 --- a/skills/codonfm-embed/SKILL.md +++ b/skills/codonfm-embed/SKILL.md @@ -1,64 +1,128 @@ --- name: codonfm-embed -description: Extract frozen CLS embeddings from public CodonFM Encodon checkpoints for coding-sequence property modeling. Use when a user explicitly asks for CodonFM or Encodon embeddings, or wants Encodon features for translation-efficiency, expression, or mRNA-stability modeling. Support Encodon embedding_prediction only; do not claim Decodon embedding support in public CodonFM v1. +description: Validate coding-sequence CSVs, extract public CodonFM/Encodon embeddings, and choose checkpoints for downstream property modeling. +license: Apache-2.0 metadata: author: "NVIDIA BioNeMo " + tags: [biology, codonfm, embeddings] --- # Extract public Encodon embeddings -Extract one frozen CLS vector per coding sequence. This workflow writes -embeddings only; it does not automatically train a downstream regressor. +## Purpose + +Extract one frozen CLS vector per coding sequence with public Encodon v1. +Support input validation, command preparation, extraction, and checkpoint +selection for translation efficiency, expression, or mRNA stability modeling. +Extraction does not automatically train a downstream regressor. + +## Prerequisites + +- Validation needs Python 3 standard library only; no GPU, weights, or API key. +- Execution needs the public CodonFM checkout, its `requirements.txt` environment, + a compatible NVIDIA GPU, and local checkpoint weights. A metadata JSON is not + a checkpoint. A `.safetensors` file needs its sibling `config.json`; `.ckpt` + checkpoints are also supported by the public loader. +- Run `python -m src.runner` from the CodonFM repository root. In an isolated + workspace, use supplied source artifacts; source paths below are relative to + that checkout or source archive, not this skill directory. + +## Inputs + +Input source precedence: explicit user prompt arguments, then supplied +files/checkpoint metadata, then inspected public runner defaults. Resolve +conflicting model names and checkpoint metadata before execution. Supplied 80M +metadata is useful for preparing an 80M command; it does not restrict an +open-ended recommendation to that size. + +Required for validation: a CSV. Required for extraction: the CSV, checkpoint, +matching model name, and output directory. Optional: context length and batch +size overrides. Checkpoint-selection questions can be answered without a CSV. + +| Input | Requirement or default | +| --- | --- | +| Sequence CSV | Columns `id`, `ref_seq`, `value`, `split`; extra columns allowed | +| `id` | Nonblank, unique IDs for unambiguous output association | +| `ref_seq` | Coding sequence, uppercase DNA `A/C/G/T`, length divisible by three; public dataset converts uppercase `U` to `T` | +| `value` | Numeric label; use `0.0` for new extraction-only data, preserve supplied labels | +| `split` | Only exact `test` values enter extraction; blank/other values are excluded | +| Checkpoint and model | Match weights/config to `encodon_80m`, `encodon_600m`, or `encodon_1b` | +| Context length | Public runner default `2048` tokens, including CLS and SEP | +| Output directory | A fresh run directory with an empty predictions directory | ## Instructions -Resolve the sequence CSV, checkpoint, and output directory from the request and -available files. Validate inputs before extraction. Execution requires the -project's ML dependencies and a compatible NVIDIA GPU. If a required resource -is unavailable, complete the available preparation and return the command with -that prerequisite identified. When extraction is requested and resources are -ready, execute and verify the embedding arrays. A request for preparation ends -with the inputs and command. If no sequences were supplied, report the required -inputs. For Decodon, inspect the [public parser](../../src/runner.py) and -[model configuration](../../src/config.py), explain the missing implementation, -and finish without attempting installation or model development. - -For a demonstration use `nvidia/NV-CodonFM-Encodon-80M-v1`, revision -`399ca9fe17b57941a7bebc6788033919b417413c`, file -`NV-CodonFM-Encodon-80M-v1.safetensors` with sibling `config.json`. -Reuse an existing checkpoint or download it when needed for the requested work. -Preserve a user's explicit checkpoint choice. - -## Preflight and inputs - -1. Confirm `src/runner.py`, `src/data/codon_bert_dataset.py`, and - `src/inference/encodon.py` exist. -2. Accept only `encodon_80m`, `encodon_600m`, or `encodon_1b`. -3. For execution require a `.ckpt`, or `.safetensors` with sibling `config.json`; - input preparation can use a planned path. -4. Require CSV columns `id`, `ref_seq`, `value`, and `split`. - -`ref_seq` must be a coding sequence. For extraction-only data, set `value` to -`0.0` and `split` to `test` on every row. Although the public dataset labels -`split` optional, its evaluation path calls the test split and fails without -that column. Normalize sequences to uppercase DNA (`A/C/G/T`) and require -lengths divisible by three. Sequences longer than `--context_length - 2` -codons are truncated rather than embedded in full. +1. **Choose the requested workflow.** For a checkpoint/performance question, + read [checkpoint selection](references/checkpoint-selection.md) and answer + from public benchmark evidence. For the strongest published downstream + results, prefer the public **1B random-mask checkpoint** when resources allow; + 80M is a demonstration or resource-constrained choice. A small labeled set + alone does not establish that 80M frozen features are better. Do not download + weights or inspect the entire source tree just to make a recommendation. +2. **Inspect supplied source only where needed.** Confirm runner/config, + `src/data/codon_bert_dataset.py`, `src/data/preprocess/codon_sequence.py`, + `src/inference/encodon.py`, or `src/utils/pred_writer.py` for the relevant + behavior. Read ZIP members with `zipfile.ZipFile.namelist()` and `.read()`; + source inspection does not need extraction. If a checkout is needed, use a + new directory from `tempfile.mkdtemp()` or `mktemp -d`, without deleting or + overwriting an existing directory. For Decodon support questions, inspect + runner/config and model/inference modules, cite the inspected files, explain + the missing public implementation, and finish there. +3. **Validate the CSV before running extraction.** Run the bundled checker + below with the intended context length. Report per-row verdicts using CSV + row numbers as well as IDs, since IDs can repeat. Separate excluded rows, + invalid inputs, duplicate-ID warnings, and truncation. Propose fixes without + silently rewriting supplied data. The checker is a preflight, not model + execution or proof of biological CDS validity. +4. **Deliver the requested preparation or execution.** For preparation, return + a complete command with resolved paths (or clearly identified prerequisites), + the test-row count, validation findings, and the output contract below. + Include all task/dataset/process flags in the final answer, even if already + shown in a tool call. For extraction, reuse/download the chosen checkpoint + when needed, execute once resources are ready, and verify the saved arrays. + If resources are missing, finish preparation and state what is missing. + +## Available Scripts + +| Script | Purpose | Arguments | +| --- | --- | --- | +| [validate_inputs.py](scripts/validate_inputs.py) | Read-only CSV validation and per-row verdicts | Required CSV path; optional `--context-length` (default `2048`) | + +Run the preflight with Python; `CODONFM_SKILL_DIR` is the directory containing this file: + +```bash +python "$CODONFM_SKILL_DIR/scripts/validate_inputs.py" "$CODONFM_DATA_PATH" \ + --context-length 2048 +``` + +The checker prints JSON. Exit `0` means no findings, `1` means row findings to +review (including exclusions/warnings), and `2` means a file/schema error. +Neither warnings nor exclusions imply that the public runner will crash. + +## Output Format + +The checker emits JSON with `total_rows`, `test_rows`, `excluded_rows`, +`context_length`, `codon_limit`, `warnings`, and `rows`. Each row records its +one-based data-row number (excluding the header), ID, split, verdict, issues, +sequence/value validity, and retained/lost codons. A file/schema error emits +`error` and `csv` instead. These are preflight findings, not generated embeddings. ## Examples -Set `CODONFM_DATA_PATH` to the sequence CSV, `CODONFM_CHECKPOINT_PATH` to the -checkpoint, and `CODONFM_RUN_DIR` to your chosen output directory: +Set `CODONFM_DATA_PATH` to the CSV, `CODONFM_CHECKPOINT_PATH` to the weights, +`CODONFM_MODEL_NAME` to the matching architecture, and `CODONFM_RUN_DIR` to a +fresh output directory. Substitute actual paths in a prepared command: ```bash python -m src.runner eval \ + --task_type embedding_prediction \ + --process_item codon_sequence \ + --dataset_name CodonBertDataset \ --exp_name embed_extract \ - --model_name encodon_80m \ + --model_name "$CODONFM_MODEL_NAME" \ --checkpoint_path "$CODONFM_CHECKPOINT_PATH" \ --data_path "$CODONFM_DATA_PATH" \ - --process_item codon_sequence \ - --dataset_name CodonBertDataset \ - --task_type embedding_prediction \ + --context_length 2048 \ --num_nodes 1 \ --num_gpus 1 \ --num_workers 0 \ @@ -67,35 +131,51 @@ python -m src.runner eval \ --predictions_output_dir "$CODONFM_RUN_DIR/predictions" ``` -For preparation requests, inspect the CSV directly against the input schema -above and report the test-row count and sequence checks. Extra columns are -allowed; extraction does not require a measured target. This does not require -the ML runtime. The command above performs extraction when resources are ready. - -The existing `--dryrun` optionally builds runtime configuration and skips -execution. It requires the ML dependencies, can create the prediction directory, -and does not read the CSV or load weights. Do not use it as evidence that inputs, -checkpoint compatibility, or embedding quality have been validated. +For a low-cost demonstration, `encodon_80m` matches +`nvidia/NV-CodonFM-Encodon-80M-v1`, revision +`399ca9fe17b57941a7bebc6788033919b417413c`, file +`NV-CodonFM-Encodon-80M-v1.safetensors` and sibling `config.json`. ## Outputs -- `embeddings_merged.npy`: shape `(number_of_rows, hidden_size)`. -- `ids_merged.npy`: IDs aligned with the embedding rows. +- Under `--predictions_output_dir`, `embeddings_merged.npy` contains frozen + final-layer CLS vectors, shape `(processed_rows, hidden_size)`. +- `ids_merged.npy` is index-aligned: embedding row `i` belongs to ID row `i`. + Use these IDs to join to the CSV; do not assume every CSV row was retained. + Duplicate IDs make that join ambiguous even when extraction succeeds. +- For the one-GPU example, verify both arrays have the expected test-row count, + embeddings are finite, and width matches checkpoint config (`1024` for 80M, + `2048` for 600M/1B). Do not fabricate arrays for a preparation-only request. -Use the checked-in Encodon notebooks as downstream-model references: +The public checkout's downstream-model references are: - `notebooks/4-EnCodon-Downstream-Task-riboNN.ipynb` - `notebooks/5-EnCodon-Downstream-Task-mRFP-expression.ipynb` - `notebooks/6-EnCodon-Downstream-Task-mRNA-stability.ipynb` -Do not reference `notebooks/te_predictor.py`, `notebooks/mfe_predictor.py`, or -Decodon notebooks because they are absent from public v1. - -## Boundaries - -- Do not use for Decodon; the public repository has no Decodon model or - inference class. +## Limitations + +- Public v1 has no Decodon model/inference implementation, Decodon notebooks, + `notebooks/te_predictor.py`, or `notebooks/mfe_predictor.py`. +- At context length `2048`, retain the first `2046` codons; any remaining + 3-prime sequence is lost. Increasing the flag does not validate a longer + context. Disclose deliberate cropping or a separate chunking/aggregation + strategy; neither is equivalent to embedding the complete sequence once. +- `--dryrun` builds runtime configuration, may create directories, and needs + ML dependencies; it reads neither the CSV nor the weights and is not input + validation. - Do not claim a benchmark-trained regressor generalizes to a new organism, cell type, or assay without new labeled validation data. - Do not invoke this skill for a generic expression-prediction request that does not mention CodonFM or Encodon. + +## Troubleshooting + +| Symptom | Cause and action | +| --- | --- | +| Missing `split` column | Eval requests the test split despite the dataset docstring calling this column optional; add an explicit split column to a corrected copy | +| Fewer output rows | Blank/non-`test` split values are silently filtered; set intended extraction rows to exact `test` in a corrected copy | +| Repeated output IDs | Duplicate input IDs are not rejected; assign unique IDs while preserving a mapping to the original rows | +| Oversized sequence | Preprocessing truncates at `context_length - 2` codons; report retained/lost lengths and agree on a sequence-handling strategy | +| Missing weights or dependencies | Complete validation/command preparation; metadata and `--dryrun` do not substitute for weights | +| Merge failure on a repeated run | The writer scans `.npy` files; use a fresh predictions directory to avoid stale shards or merged arrays | diff --git a/skills/codonfm-embed/references/checkpoint-selection.md b/skills/codonfm-embed/references/checkpoint-selection.md new file mode 100644 index 0000000..3133f1a --- /dev/null +++ b/skills/codonfm-embed/references/checkpoint-selection.md @@ -0,0 +1,42 @@ +# Choosing a public Encodon checkpoint + +Use this reference for checkpoint recommendations and performance questions. +Honor a requested checkpoint and resource constraints; supplied example metadata +is not evidence that it is the best model for a different task. + +| User objective | Starting choice | Reason | +| --- | --- | --- | +| Strongest published translation-efficiency/expression results | [nvidia/NV-CodonFM-Encodon-1B-v1](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1), `encodon_1b` | Largest released random-mask model; strongest downstream results in the preprint | +| Codon-frequency-weighted masking | [nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1), also `encodon_1b` | Alternative masking objective; compare on task-specific validation data | +| Quick demonstration or restricted memory/latency | 80M (`encodon_80m`), or 600M (`encodon_600m`) | Resource tradeoff, not evidence of superior embedding quality | + +The [CodonFM preprint, Figure 5 and accompanying text, page 11](https://research.nvidia.com/labs/dbr/assets/data/manuscripts/nv-codonfm-preprint.pdf) +compares random-forest regressors trained on frozen pretrained embeddings. It +reports the strongest downstream correlation and explained variance for the 1B +model. Cdwt embeddings depend less on simple features such as GC content and can +perform worse on tasks dominated by those features. Prefer random-mask 1B for +this benchmark-driven recommendation; Cdwt is not universally better. + +Keep the reported metrics distinct: Figure 5A's caption specifies mean 10-fold +cross-validation **R²** for translation efficiency; Figure 5B specifies +**Spearman correlation** for mRFP expression. An R² value is not Pearson's r or +Spearman's rho. Cite the paper/figure for performance claims, not runner source +or checkpoint configuration, which contain no benchmark results. Quote a +number only after checking the corresponding metric, panel, and dataset; label +a visually estimated value as approximate. + +For a new ~200-sequence labeled set, no numerical correlation is established by +these benchmarks. Recommend 1B as an evidence-based starting point, explain the +published advantage, and separate it from expected performance on the new assay. +Suggest cross-validation of a lightweight regressor on frozen features, grouping +related sequences to reduce leakage, and reporting uncertainty. Small training +sets motivate controlling regressor complexity; they do not by themselves +justify choosing the smallest frozen backbone. Do not promise `r ≈ 0.6` or infer +correlation by taking the square root of the paper's cross-validation R². + +The [public repository's model table](https://github.com/NVIDIA-Digital-Bio/CodonFM#pre-trained-models) +lists 80M, 600M, 1B, and Cdwt-1B. Parser options alone do not establish that 5B or +10B weights are released. The `TE` checkpoint family refers to the accelerated +**Transformer Engine** implementation, not a translation-efficiency-trained +checkpoint. Keep the original public runner and its compatible checkpoints +together; changing to the accelerated recipe is a separate runtime choice. diff --git a/skills/codonfm-embed/scripts/validate_inputs.py b/skills/codonfm-embed/scripts/validate_inputs.py new file mode 100644 index 0000000..b02442c --- /dev/null +++ b/skills/codonfm-embed/scripts/validate_inputs.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 +"""Read-only CSV preflight for public Encodon embedding extraction. + +Usage: python validate_inputs.py sequences.csv [--context-length 2048] +Arguments: input CSV path; optional context length including CLS and SEP. +Output: JSON summary and per-row verdicts on stdout, or an error object. +Exit codes: 0 clean, 1 row findings to review, 2 file/schema/argument error. + +Uses only the standard library. Reports input problems without claiming to run +the model or reproducing every pandas/tokenizer coercion. Never writes inputs. +""" + +import argparse +from collections import Counter +import csv +import json +import math +from pathlib import Path + + +REQUIRED_COLUMNS = {"id", "ref_seq", "value", "split"} +DEFAULT_CONTEXT_LENGTH = 2048 +SPECIAL_TOKEN_COUNT = 2 +CODON_WIDTH = 3 + + +def validate_csv(path: Path, context_length: int = DEFAULT_CONTEXT_LENGTH) -> dict: + """Describe split selection, sequence validity, ID ambiguity, and truncation.""" + if context_length <= SPECIAL_TOKEN_COUNT: + raise ValueError("context length must allow CLS, at least one codon, and SEP") + with path.open(encoding="utf-8-sig", newline="") as handle: + reader = csv.DictReader(handle, strict=True) + columns = reader.fieldnames or [] + missing = sorted(REQUIRED_COLUMNS - set(columns)) + if missing: + raise ValueError(f"missing required columns for eval: {', '.join(missing)}") + if len(set(columns)) != len(columns): + raise ValueError("duplicate CSV column names") + rows = list(reader) + if any(None in row or any(v is None for v in row.values()) for row in rows): + raise ValueError("CSV row width does not match header") + + ids = Counter(row["id"] for row in rows) + limit = context_length - SPECIAL_TOKEN_COUNT + verdicts = [] + for number, row in enumerate(rows, start=1): + selected = row["split"] == "test" + issues = [] + if not selected: + issues.append("excluded: split must be exactly 'test' to enter extraction") + if not row["id"].strip(): + issues.append("blank id: assign a unique nonblank ID") + elif ids[row["id"]] > 1: + issues.append("duplicate id: extraction does not reject it, but output-to-source joins are ambiguous; assign unique IDs") + + sequence = row["ref_seq"] + dna = sequence.replace("U", "T") + sequence_valid = bool(dna) and set(dna) <= set("ACGT") and len(dna) % CODON_WIDTH == 0 + if not sequence_valid: + issues.append("invalid sequence for this preflight: require nonempty uppercase A/C/G/T (or U) and a length divisible by three; correct a copy") + try: + value_valid = math.isfinite(float(row["value"])) + except ValueError: + value_valid = False + if not value_valid: + issues.append("invalid value: supply a finite numeric label or 0.0 for extraction-only data") + + codons = len(dna) // CODON_WIDTH if sequence_valid else None + retained = min(codons, limit) if selected and sequence_valid else None + lost = codons - retained if retained is not None else None + if lost: + issues.append(f"truncated: first {retained} codons retained; last {lost} codons ({CODON_WIDTH * lost} nucleotides) lost") + if not selected: + verdict = "excluded" + elif not (sequence_valid and value_valid): + verdict = "needs_correction" + elif lost: + verdict = "truncated" + else: + verdict = "processes" + verdicts.append({ + "row": number, + "id": row["id"], + "split": row["split"], + "selected_for_test": selected, + "nucleotides": len(sequence), + "codons": codons, + "value": row["value"], + "sequence_valid": sequence_valid, + "value_valid": value_valid, + "retained_codons": retained, + "lost_codons": lost, + "verdict": verdict, + "issues": issues, + }) + selected_rows = sum(item["selected_for_test"] for item in verdicts) + return { + "csv": str(path), + "context_length": context_length, + "codon_limit": limit, + "total_rows": len(rows), + "test_rows": selected_rows, + "excluded_rows": len(rows) - selected_rows, + "warnings": [] if selected_rows else ["no rows have split='test'; no embeddings can be produced"], + "rows": verdicts, + } + + +def main(argv=None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("csv", type=Path, help="input CSV; never modified") + parser.add_argument("--context-length", type=int, default=DEFAULT_CONTEXT_LENGTH) + args = parser.parse_args(argv) + try: + report = validate_csv(args.csv, args.context_length) + except (OSError, UnicodeError, csv.Error, ValueError) as error: + print(json.dumps({"error": str(error), "csv": str(args.csv)})) + return 2 + print(json.dumps(report, indent=2)) + return int(bool(report["warnings"]) or any(row["issues"] for row in report["rows"])) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/codonfm-embed/tests/test_validate_inputs.py b/skills/codonfm-embed/tests/test_validate_inputs.py new file mode 100644 index 0000000..3a4d8fc --- /dev/null +++ b/skills/codonfm-embed/tests/test_validate_inputs.py @@ -0,0 +1,121 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 +"""Exercise observable CSV verdicts and the read-only command interface.""" + +import csv +from contextlib import redirect_stdout, redirect_stderr +import importlib.util +import io +import json +from pathlib import Path +import tempfile +import unittest + + +SKILL = Path(__file__).resolve().parents[1] +SCRIPT = SKILL / "scripts" / "validate_inputs.py" +spec = importlib.util.spec_from_file_location("validate_inputs", SCRIPT) +checker = importlib.util.module_from_spec(spec) +spec.loader.exec_module(checker) + + +class ValidateInputsTest(unittest.TestCase): + def write_csv(self, directory, rows, columns=("id", "ref_seq", "value", "split")): + path = Path(directory) / "input.csv" + with path.open("w", newline="") as handle: + writer = csv.writer(handle) + writer.writerow(columns) + writer.writerows(rows) + return path + + def test_supplied_clean_rows_and_no_writes(self): + path = SKILL / "evals" / "files" / "sequences.csv" + before = path.read_bytes() + report = checker.validate_csv(path) + self.assertEqual(report["test_rows"], 2) + self.assertEqual([row["verdict"] for row in report["rows"]], ["processes"] * 2) + self.assertTrue(all(not row["issues"] for row in report["rows"])) + self.assertEqual(path.read_bytes(), before) + + def test_supplied_edge_cases(self): + path = SKILL / "evals" / "files" / "sequences_edge_cases.csv" + before = path.read_bytes() + report = checker.validate_csv(path) + self.assertEqual(report["test_rows"], 3) + self.assertEqual(report["excluded_rows"], 1) + rows = report["rows"] + self.assertEqual([row["verdict"] for row in rows], ["processes", "excluded", "processes", "truncated"]) + self.assertEqual([row["row"] for row in rows], [1, 2, 3, 4]) + self.assertTrue(any("duplicate id" in issue for issue in rows[0]["issues"])) + self.assertTrue(any("duplicate id" in issue for issue in rows[2]["issues"])) + self.assertEqual((rows[3]["codons"], rows[3]["retained_codons"], rows[3]["lost_codons"]), (2050, 2046, 4)) + self.assertEqual(path.read_bytes(), before) + + def test_configurable_context_boundary_and_rna(self): + with tempfile.TemporaryDirectory() as directory: + path = self.write_csv(directory, [ + ["boundary", "AUG" * 6, "0.0", "test"], + ["over", "ATG" * 7, "0.0", "test"], + ]) + rows = checker.validate_csv(path, context_length=8)["rows"] + self.assertEqual(rows[0]["verdict"], "processes") + self.assertEqual(rows[0]["lost_codons"], 0) + self.assertEqual(rows[1]["lost_codons"], 1) + + def test_strict_split_filter_including_invalid_excluded_sequence(self): + with tempfile.TemporaryDirectory() as directory: + path = self.write_csv(directory, [ + [str(n), "bad-sequence", "not-a-number", split] + for n, split in enumerate(["", "train", "Test", "test "]) + ]) + report = checker.validate_csv(path) + self.assertEqual(report["test_rows"], 0) + self.assertTrue(report["warnings"]) + self.assertTrue(all(row["verdict"] == "excluded" for row in report["rows"])) + self.assertTrue(all(row["retained_codons"] is None for row in report["rows"])) + + def test_invalid_sequences_and_labels(self): + with tempfile.TemporaryDirectory() as directory: + path = self.write_csv(directory, [ + ["empty", "", "0", "test"], + ["partial", "ATGA", "0", "test"], + ["lower", "atg", "0", "test"], + ["ambiguous", "ANN", "0", "test"], + ["label", "ATG", "NaN", "test"], + ]) + report = checker.validate_csv(path) + self.assertTrue(all(row["verdict"] == "needs_correction" for row in report["rows"])) + + def test_missing_split_and_bad_row_width(self): + with tempfile.TemporaryDirectory() as directory: + path = self.write_csv(directory, [["a", "ATG", "0"]], columns=("id", "ref_seq", "value")) + with self.assertRaisesRegex(ValueError, "split"): + checker.validate_csv(path) + path = self.write_csv(directory, [["a", "ATG", "0", "test", "extra"]]) + with self.assertRaisesRegex(ValueError, "width"): + checker.validate_csv(path) + + def test_empty_dataset_and_invalid_context(self): + with tempfile.TemporaryDirectory() as directory: + path = self.write_csv(directory, []) + report = checker.validate_csv(path) + self.assertEqual(report["rows"], []) + self.assertTrue(report["warnings"]) + with self.assertRaisesRegex(ValueError, "context length"): + checker.validate_csv(path, 2) + + def test_cli_exit_codes_and_json(self): + for name, expected in [("sequences.csv", 0), ("sequences_edge_cases.csv", 1), ("missing.csv", 2)]: + with self.subTest(name=name): + stdout, stderr = io.StringIO(), io.StringIO() + argv = [str(SKILL / "evals" / "files" / name)] + with redirect_stdout(stdout), redirect_stderr(stderr): + code = checker.main(argv) + self.assertEqual(code, expected) + report = json.loads(stdout.getvalue()) + self.assertEqual("error" in report, expected == 2) + self.assertFalse(stderr.getvalue()) + + +if __name__ == "__main__": + unittest.main() From 2d22010d08462c23b9490d2ee550ae74deabb59b Mon Sep 17 00:00:00 2001 From: nvskills-svc-account Date: Wed, 7 Oct 2026 18:48:52 +0000 Subject: [PATCH 13/13] Attach NVSkills validation signatures Signed-off-by: nvskills-svc-account --- skills/codonfm-embed/BENCHMARK.md | 53 ++++++++++++++------------- skills/codonfm-embed/skill-card.md | 53 ++++++++++++++------------- skills/codonfm-embed/skill.oms.sig | 2 +- skills/codonfm-finetune/BENCHMARK.md | 32 ++++++++-------- skills/codonfm-finetune/skill-card.md | 41 ++++++++++----------- skills/codonfm-finetune/skill.oms.sig | 2 +- skills/codonfm-score/BENCHMARK.md | 30 +++++++-------- skills/codonfm-score/skill-card.md | 41 ++++++++++----------- skills/codonfm-score/skill.oms.sig | 2 +- skills/codonfm-setup/BENCHMARK.md | 30 +++++++-------- skills/codonfm-setup/skill-card.md | 50 +++++++++++++------------ skills/codonfm-setup/skill.oms.sig | 2 +- 12 files changed, 171 insertions(+), 167 deletions(-) diff --git a/skills/codonfm-embed/BENCHMARK.md b/skills/codonfm-embed/BENCHMARK.md index 0dde7ca..2764072 100644 --- a/skills/codonfm-embed/BENCHMARK.md +++ b/skills/codonfm-embed/BENCHMARK.md @@ -1,17 +1,19 @@ # Skill Benchmark: codonfm-embed -> **Overall verdict: NEUTRAL — One or more dimensions remain below PASS** +> ✅ **Overall verdict: PASS — Recommended for publication** -Live evaluation did not show a material gain or regression. Collect more evidence or improve the skill before making a publication decision. +## Publication Recommendation + +Recommended for publication based on the completed evaluation evidence in this report. ## Evaluation Metadata - Skill: `codonfm-embed` -- Evaluation date: 2026-10-02 +- Evaluation date: 2026-10-07 - Evaluator version: `1.5.6` - Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`), Codex (`openai/openai/gpt-5.5`) -- Tasks: 2 evaluation tasks (2 positive) -- Dataset digest: `sha256:13e6b2a6ffaa03dba4985cf78c69ce0dfef33331f5fba362890e08c6cf187db2` (skill-evaluator-dataset-snapshot/1) +- Tasks: 4 evaluation tasks (4 positive) +- Dataset digest: `sha256:8ef619d6a9074d8b7fdc6224f220bb79aad6e37e2222f3290580290b7a6afbeb` (skill-evaluator-dataset-snapshot/1) - Attempts per task: 1 - Environment: `k8s-sandbox` - Tier 2 evidence: required for publication @@ -33,12 +35,12 @@ The three-tier evaluation checks whether the skill: | Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 91.5% — baseline ran, but no comparable score was available; uplift unavailable | 78.0% — baseline ran, but no comparable score was available; uplift unavailable | -| Security | 100.0% → 100.0% (±0.0 points) | 0.0% → 50.0% (+50.0 points) | +| Overall | 96.1% — baseline ran, but no comparable score was available; uplift unavailable | 93.4% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 75.0% → 100.0% (+25.0 points) | 75.0% → 100.0% (+25.0 points) | | Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | -| Discoverability | 92.5% — baseline ran, but no comparable score was available; uplift unavailable | 75.0% — baseline ran, but no comparable score was available; uplift unavailable | -| Effectiveness | 100.0% → 81.3% (-18.7 points) | 100.0% → 81.3% (-18.7 points) | -| Efficiency | 83.6% — baseline ran, but no comparable score was available; uplift unavailable | 83.9% — baseline ran, but no comparable score was available; uplift unavailable | +| Discoverability | 96.3% — baseline ran, but no comparable score was available; uplift unavailable | 86.3% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 85.6% → 90.6% (+5.0 points) | 98.8% → 93.8% (-5.0 points) | +| Efficiency | 93.6% — baseline ran, but no comparable score was available; uplift unavailable | 86.9% — baseline ran, but no comparable score was available; uplift unavailable | **How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. @@ -50,13 +52,17 @@ Actual Tier 3 execution usage is reported for every observed agent/case pair and | Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | |---|---|---:|---:|---:|---:|---| -| claude-code | All cases | 1,552,466 | 2,540,319 | -987,853 | -38.89% | skill 2/2; base 2/2 | -| claude-code | codonfm-embed-001 | 1,191,884 | 1,670,815 | -478,931 | -28.66% | skill 1/1; base 1/1 | -| claude-code | codonfm-embed-002 | 360,582 | 869,504 | -508,922 | -58.53% | skill 1/1; base 1/1 | -| codex | All cases | 756,363 | 747,441 | +8,922 | +1.19% | skill 2/2; base 2/2 | -| codex | codonfm-embed-001 | 347,201 | 309,716 | +37,485 | +12.10% | skill 1/1; base 1/1 | -| codex | codonfm-embed-002 | 409,162 | 437,725 | -28,563 | -6.53% | skill 1/1; base 1/1 | -| ALL AGENTS | Dataset aggregate | 2,308,829 | 3,287,760 | -978,931 | -29.78% | skill 4/4; base 4/4 | +| claude-code | All cases | 1,152,349 | 5,695,914 | -4,543,565 | -79.77% | skill 4/4; base 4/4 | +| claude-code | codonfm-embed-001 | 544,672 | 1,934,434 | -1,389,762 | -71.84% | skill 1/1; base 1/1 | +| claude-code | codonfm-embed-002 | 243,222 | 950,364 | -707,142 | -74.41% | skill 1/1; base 1/1 | +| claude-code | codonfm-embed-003 | 265,873 | 2,038,952 | -1,773,079 | -86.96% | skill 1/1; base 1/1 | +| claude-code | codonfm-embed-004 | 98,582 | 772,164 | -673,582 | -87.23% | skill 1/1; base 1/1 | +| codex | All cases | 487,427 | 1,838,975 | -1,351,548 | -73.49% | skill 4/4; base 4/4 | +| codex | codonfm-embed-001 | 106,660 | 518,881 | -412,221 | -79.44% | skill 1/1; base 1/1 | +| codex | codonfm-embed-002 | 159,116 | 322,872 | -163,756 | -50.72% | skill 1/1; base 1/1 | +| codex | codonfm-embed-003 | 157,215 | 924,873 | -767,658 | -83.00% | skill 1/1; base 1/1 | +| codex | codonfm-embed-004 | 64,436 | 72,349 | -7,913 | -10.94% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 1,639,776 | 7,534,889 | -5,895,113 | -78.24% | skill 8/8; base 8/8 | Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. @@ -64,21 +70,18 @@ Prompt tokens include cached reads, so total tokens are `prompt + completion` (c | Tier | Purpose | Status | Evidence | |---|---|---|---| -| Tier 1 | Static validation | **PASSED WITH OBSERVATIONS** | 11 validator(s); 7 finding(s) | +| Tier 1 | Static validation | **PASSED WITH OBSERVATIONS** | 11 validator(s); 3 finding(s) | | Tier 2 | Semantic deduplication | **PASSED** | 2 validator(s); 0 finding(s) | -| Tier 3 | Live agent evaluation | **NEUTRAL** | 2 agent(s); 2 task(s) | +| Tier 3 | Live agent evaluation | **PASS** | 2 agent(s); 4 task(s) | ## Findings and Observations
Show detailed findings and successful checks -- **MEDIUM** QUALITY/quality_correctness: SKILL_SPEC recommended field missing: 'metadata.tags' (`skills/codonfm-embed/SKILL.md`) -- **LOW** QUALITY/quality_discoverability: Description very long (373 chars, recommend 50-150) (`skills/codonfm-embed/SKILL.md`) -- **LOW** QUALITY/quality_discoverability: No '## Purpose' section (`skills/codonfm-embed/SKILL.md`) -- **LOW** QUALITY/quality_reliability: No prerequisites/requirements documented (`skills/codonfm-embed/SKILL.md`) -- **LOW** QUALITY/quality_reliability: No limitations documented (`skills/codonfm-embed/SKILL.md`) -- 2 additional finding(s) are available in the full evaluation artifacts. +- **MEDIUM** QUALITY/quality_correctness: Instructions don't mention 'run_script' (`skills/codonfm-embed/SKILL.md`) +- **MEDIUM** SECURITY/Unknown (LP3): MCP Least Privilege: Without declared permissions the skill's intent is opaque and cannot be validated. (`SKILL.md:1`) +- **LOW** SCRIPT_LINT/magic_numbers: validate_inputs.py contains magic numbers (`skills/codonfm-embed/scripts/validate_inputs.py`)
diff --git a/skills/codonfm-embed/skill-card.md b/skills/codonfm-embed/skill-card.md index c9708dc..64cfb58 100644 --- a/skills/codonfm-embed/skill-card.md +++ b/skills/codonfm-embed/skill-card.md @@ -1,5 +1,5 @@ ## Description:
-Extract frozen CLS embeddings from public CodonFM Encodon checkpoints for coding-sequence property modeling.
+Validate coding-sequence CSVs, extract public CodonFM/Encodon embeddings, and choose checkpoints for downstream property modeling.
This skill is ready for commercial/non-commercial use.
@@ -9,14 +9,14 @@ NVIDIA
### License/Terms of Use:
Apache 2.0
## Use Case:
-Developers and bioinformatics engineers who need to extract frozen CLS vector embeddings from public CodonFM Encodon checkpoints for downstream codon-sequence property modeling tasks such as translation efficiency, expression, or mRNA stability prediction.
+Developers and engineers working with codon sequences use this skill to validate CSV inputs, extract frozen Encodon embeddings, and select model checkpoints for downstream property modeling such as translation efficiency and mRNA stability prediction.
### Deployment Geography for Use:
Global
## Requirements / Dependencies:
-**Requires API Key or External Credential:** [Not Specified]
-**Credential Type(s):** [None identified]
+**Requires API Key or External Credential:** [No]
+**Credential Type(s):** [None]
Do not include secrets in prompts/logs/output; use least-privilege credentials; rotate keys as appropriate.
@@ -25,19 +25,20 @@ Risk: Review before execution as proposals could introduce incorrect or misleadi Mitigation: Review and scan skill before deployment.
## Reference(s):
-- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
-- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
-- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
-- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
+- [Checkpoint Selection Guide](references/checkpoint-selection.md)
+- [NV-CodonFM-Encodon-1B-v1 (HuggingFace)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
+- [NV-CodonFM-Encodon-80M-v1 (HuggingFace)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
+- [NV-CodonFM-Encodon-600M-v1 (HuggingFace)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
+- [NV-CodonFM-Encodon-Cdwt-1B-v1 (HuggingFace)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
- [CodonFM Encodon on NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
-- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
+- [CodonFM Preprint](https://research.nvidia.com/labs/dbr/assets/data/manuscripts/nv-codonfm-preprint.pdf)
## Skill Output:
-**Output Type(s):** [Files, Shell commands]
-**Output Format:** [NumPy arrays (.npy) and Markdown with inline bash code blocks]
+**Output Type(s):** [Shell commands, Analysis]
+**Output Format:** [Markdown with inline bash code blocks and JSON]
**Output Parameters:** [1D]
-**Other Properties Related to Output:** [Outputs embeddings_merged.npy (shape: rows × hidden_size) and ids_merged.npy aligned by row index]
+**Other Properties Related to Output:** [None]
## Evaluation Agents Used:
- Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`)
@@ -46,23 +47,23 @@ Mitigation: Review and scan skill before deployment.
## Evaluation Tasks:
-2 evaluation tasks (2 positive) executed in isolated sandbox pods.
+Evaluated against 4 evaluation tasks (4 positive) in isolated sandbox pods, using evaluator version 1.5.6.
## Evaluation Metrics Used:
Reported benchmark dimensions:
-- Security: Whether the skill is safe to use, checking for unsafe operations, secret leakage, and unauthorized access.
-- Correctness: Whether the answer is correct against the reference answer.
-- Discoverability: Whether the right skill was loaded when needed, including skill selection and decoy avoidance.
-- Effectiveness: Whether the skill helped complete the task, measured by goal completion and expected workflow adherence.
-- Efficiency: Whether the skill avoided wasted tool calls and token usage.
+- Security: Whether the skill is safe to use: checks for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Whether the final answer is correct against the reference answer.
+- Discoverability: Whether the expected skill was selected and activated when needed, and decoys were avoided.
+- Effectiveness: Whether the skill helped complete the user's goal (50% goal accuracy + 50% expected workflow adherence).
+- Efficiency: Whether the skill avoided wasted tool calls and token usage (50% tool-call productivity + 50% token efficiency).
Underlying evaluation signals used in this run:
- `security`: Checks for unsafe operations, secret leakage, and unauthorized access.
-- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
- `accuracy`: Final-answer correctness against the reference answer.
+- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
- `goal_accuracy`: Whether the user's goal was achieved.
- `behavior_check`: Whether the expected workflow behavior was followed.
-- `skill_efficiency`: Tool-call productivity.
+- `skill_efficiency`: Tool-call productivity measured against expected tool-call patterns.
- `token_efficiency`: Actual uncached prompt plus completion token usage.
@@ -70,15 +71,15 @@ Underlying evaluation signals used in this run:
## Evaluation Results:
| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 91.5% — uplift unavailable | 78.0% — uplift unavailable | -| Security | 100.0% → 100.0% (±0.0 points) | 0.0% → 50.0% (+50.0 points) | +| Overall | 96.1% | 93.4% | +| Security | 75.0% → 100.0% (+25.0 points) | 75.0% → 100.0% (+25.0 points) | | Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | -| Discoverability | 92.5% — uplift unavailable | 75.0% — uplift unavailable | -| Effectiveness | 100.0% → 81.3% (-18.7 points) | 100.0% → 81.3% (-18.7 points) | -| Efficiency | 83.6% — uplift unavailable | 83.9% — uplift unavailable | +| Discoverability | 96.3% | 86.3% | +| Effectiveness | 85.6% → 90.6% (+5.0 points) | 98.8% → 93.8% (-5.0 points) | +| Efficiency | 93.6% | 86.9% | ## Skill Version(s):
-29194e8 (source: git SHA, committed 2026-09-18)
+be43117 (source: git SHA, committed 2026-10-07)
## Ethical Considerations:
NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
diff --git a/skills/codonfm-embed/skill.oms.sig b/skills/codonfm-embed/skill.oms.sig index edc369e..03f18f5 100644 --- a/skills/codonfm-embed/skill.oms.sig +++ b/skills/codonfm-embed/skill.oms.sig @@ -1 +1 @@ -{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1lbWJlZCIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI0YWMyNGRlODZjMjMxOTkwNjNlZWEwYzdhYThkMzAxMTIzY2NlOGEyN2E1ZGI4MmI0MDc5M2VmZTA3YWY0NDc4IgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmYWU4YWU1NTkzN2RkYmVmOWJhNDMxM2Y2YWZjM2QzNTgwMDNmYzNkNTFmZGM0Y2RjMGI3YmQwNTgxNTQ2NTNmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM4YmFjZWUzOWI0ODk4MmQwMzJjMTZmMDllODgxM2ZmZThiN2FhZjM4Y2FjNGRkNjAxNTVkYjVhMDcyNjU1YjQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViZDkxNWJiM2VkMTFjMDdjZjQzMTc4YTMwYTJmYTJlZDdmZTg5ODA2ZDZlMTI0MThmY2YxODFmNmJjYTMxOTgiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9ldmFscy5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhZjliMjMxYmI2NjBhNTkyMzlmODU5NTc1ODZhNWMzZTNkMDMwZGUxNjEyMzgwZDU2ZGI4MWViNDU2NzU5ODVkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvY29kb25mbV9zb3VyY2UuemlwIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmMGMxMjJmMTlhMmQzMTZmZTk1Y2NmYWEwYWM1MWJkNjEzY2ZhYmRkMDlmZDg5ZmJiNzY2NTYyYTVmMGVlZDI3IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvZW5jb2Rvbl9jaGVja3BvaW50Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy9zZXF1ZW5jZXMuY3N2IiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyZWQ2OGZlZmQ2MWUzNjFkZDIxZDBiYzI4YTdkZGRlZjMxZTNlOWI0MmRkNjEwMGY1MzRlOTA2OTdjNzk3ZTc5IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTZkMTA2MGIzMTAzOGExNjdmNWNmOWQ3MTYxYzYzOWM4M2U3ZDA3ZWI1NzYxNTliNWRiMWIwMjA4MWVlYmM4OSIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAibWV0aG9kIjogImZpbGVzIiwKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGh1YiIsCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0IiwKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiCiAgICAgIF0sCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGQCMCQ5CSeV0bGAa9NEXHN7AbCDGfCDbDZf0OborrK56Y8jpGrpu9f1NGjDzVaVfNCDfAIwZWnJAYQe3lAmBN3XWFNizNXrNcpR/no3w2OZETi3150UtFr8otFqJUh8SLKzJkYi","keyid":""}]}} \ No newline at end of file +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1lbWJlZCIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIyZTNkNzljZDIwODczY2RmYTliMTgyMzVhMTZlYjEzZGFhYTY0MTEzNjU4MWRlMmE0ZTI1N2JmNmRjZTNjZjYzIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMGJmMmM4ZjBkMWIzN2E1OWM3MGU5YmNhY2I5ZjgxYmMwMGZlMWJlMTJlOWM2MjM4MDAwZjA5ZmUyNTMwODg2OSIsCiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMGQ4ZDQzMGE0OGExZjBjNjMzMjViMDZlMDVkM2RkNmM5NDgwNjNkNjYyNWRmNjUxMDBkMGMxNjJmMzlhMjRhNyIsCiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlYmQ5MTViYjNlZDExYzA3Y2Y0MzE3OGEzMGEyZmEyZWQ3ZmU4OTgwNmQ2ZTEyNDE4ZmNmMTgxZjZiY2EzMTk4IiwKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjOWQ5YmZhNmJjYzUzMGJhNWViYjk0MDZmNWRmYWZkMzU5OTM4ODA0MTM1MDBjYTBkYzI2YWFkYjEzMDU0MTI5IiwKICAgICAgICAibmFtZSI6ICJldmFscy9ldmFscy5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZjBjMTIyZjE5YTJkMzE2ZmU5NWNjZmFhMGFjNTFiZDYxM2NmYWJkZDA5ZmQ4OWZiYjc2NjU2MmE1ZjBlZWQyNyIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvY29kb25mbV9zb3VyY2UuemlwIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjk4ZmEzNTQxNDAwMzNlOWFhODU5MWNkMjg2MDcwM2E0ZmNkYzJlYjI3ZWRjOGYyZjA3NjBjNDA4M2U1NjNlZSIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvZW5jb2Rvbl9jaGVja3BvaW50Lmpzb24iCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyZWQ2OGZlZmQ2MWUzNjFkZDIxZDBiYzI4YTdkZGRlZjMxZTNlOWI0MmRkNjEwMGY1MzRlOTA2OTdjNzk3ZTc5IiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy9zZXF1ZW5jZXMuY3N2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjlkZTgyM2I0ZGE2ZmY3ZDY1ZmFmYTkyZjBmYTQ3ZjdkZDRjMGI3MTI3ZGRhMzlmYTA0MmJiNWFlODJiZWQzNCIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvc2VxdWVuY2VzX2VkZ2VfY2FzZXMuY3N2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjhjZWQ5MjFmMWVjYjQ2MjU2MWNkNDExNzlkMTYwYzk4N2U0Y2QzYzk4Mjg2N2M1NzYxMjUyNTVjMTA0MTJlMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9jaGVja3BvaW50LXNlbGVjdGlvbi5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImE3MGZmYTA2ZjhiZmJkNWY0YWQ1OGFiOGYzZjBmYjNmMDYwMmE2ZmIzMWU4NGE4Mjg1YTA4M2I3OWZhMzFjNTIiLAogICAgICAgICJuYW1lIjogInNjcmlwdHMvdmFsaWRhdGVfaW5wdXRzLnB5IgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjRkNDZhNDA0YjYxZDQ1ZGFjZGRjNDk3YTQ3ODUxZjg2NjQzODRmZmQ4MmU4NjQ3ZTFjZDc2MDgwYjdmZjlhOSIsCiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjU3NmJmOTI3ZTljNjA2NjhkOWNkMjdmYWRjYWE2Zjg5ZWM5YjdjZmI3MmViYzMxNjU3NmY1YjA3YTA1NTYzYmMiLAogICAgICAgICJuYW1lIjogInRlc3RzL3Rlc3RfdmFsaWRhdGVfaW5wdXRzLnB5IgogICAgICB9CiAgICBdLAogICAgInNlcmlhbGl6YXRpb24iOiB7CiAgICAgICJhbGxvd19zeW1saW5rcyI6IGZhbHNlLAogICAgICAibWV0aG9kIjogImZpbGVzIiwKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiLAogICAgICAgICIuZ2l0aWdub3JlIiwKICAgICAgICAiLmdpdGh1YiIsCiAgICAgICAgIi5naXQiCiAgICAgIF0sCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IgogICAgfQogIH0KfQ==","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCME1fdEyorazZs3RPQjQ0PrLkZjnMy53fYqp5gHPdjd4nu2N95I1eI8qI9hxxq5mQRAIxALV0IoPK5s8estrH+x4hKfMvZRQHp2Jxo3xaalm7Ux7+p9qLxrs6uTRNzfHuWB3F1Q==","keyid":""}]}} \ No newline at end of file diff --git a/skills/codonfm-finetune/BENCHMARK.md b/skills/codonfm-finetune/BENCHMARK.md index 04a87d1..6250f63 100644 --- a/skills/codonfm-finetune/BENCHMARK.md +++ b/skills/codonfm-finetune/BENCHMARK.md @@ -9,7 +9,7 @@ Recommended for publication based on the completed evaluation evidence in this r ## Evaluation Metadata - Skill: `codonfm-finetune` -- Evaluation date: 2026-10-02 +- Evaluation date: 2026-10-07 - Evaluator version: `1.5.6` - Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-4-8`), Codex (`openai/openai/gpt-5.5`) - Tasks: 3 evaluation tasks (3 positive) @@ -35,12 +35,12 @@ The three-tier evaluation checks whether the skill: | Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 89.9% — baseline ran, but no comparable score was available; uplift unavailable | 87.9% — baseline ran, but no comparable score was available; uplift unavailable | -| Security | 100.0% → 100.0% (±0.0 points) | 33.3% → 66.7% (+33.4 points) | +| Overall | 85.0% — baseline ran, but no comparable score was available; uplift unavailable | 82.8% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 100.0% → 66.7% (-33.3 points) | 33.3% → 66.7% (+33.4 points) | | Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | -| Discoverability | 87.7% — baseline ran, but no comparable score was available; uplift unavailable | 91.0% — baseline ran, but no comparable score was available; uplift unavailable | -| Effectiveness | 83.3% → 80.0% (-3.3 points) | 86.7% → 93.3% (+6.6 points) | -| Efficiency | 82.0% — baseline ran, but no comparable score was available; uplift unavailable | 88.5% — baseline ran, but no comparable score was available; uplift unavailable | +| Discoverability | 91.7% — baseline ran, but no comparable score was available; uplift unavailable | 76.7% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 86.7% → 86.7% (±0.0 points) | 96.7% → 90.0% (-6.7 points) | +| Efficiency | 80.0% — baseline ran, but no comparable score was available; uplift unavailable | 80.7% — baseline ran, but no comparable score was available; uplift unavailable | **How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. @@ -52,15 +52,15 @@ Actual Tier 3 execution usage is reported for every observed agent/case pair and | Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | |---|---|---:|---:|---:|---:|---| -| claude-code | All cases | 2,361,347 | 6,416,159 | -4,054,812 | -63.20% | skill 3/3; base 3/3 | -| claude-code | codonfm-finetune-001 | 1,038,311 | 3,718,487 | -2,680,176 | -72.08% | skill 1/1; base 1/1 | -| claude-code | codonfm-finetune-002 | 676,297 | 1,890,699 | -1,214,402 | -64.23% | skill 1/1; base 1/1 | -| claude-code | codonfm-finetune-003 | 646,739 | 806,973 | -160,234 | -19.86% | skill 1/1; base 1/1 | -| codex | All cases | 794,045 | 1,840,603 | -1,046,558 | -56.86% | skill 3/3; base 3/3 | -| codex | codonfm-finetune-001 | 351,214 | 721,661 | -370,447 | -51.33% | skill 1/1; base 1/1 | -| codex | codonfm-finetune-002 | 223,229 | 483,694 | -260,465 | -53.85% | skill 1/1; base 1/1 | -| codex | codonfm-finetune-003 | 219,602 | 635,248 | -415,646 | -65.43% | skill 1/1; base 1/1 | -| ALL AGENTS | Dataset aggregate | 3,155,392 | 8,256,762 | -5,101,370 | -61.78% | skill 6/6; base 6/6 | +| claude-code | All cases | 2,259,711 | 5,318,079 | -3,058,368 | -57.51% | skill 3/3; base 3/3 | +| claude-code | codonfm-finetune-001 | 975,823 | 2,626,474 | -1,650,651 | -62.85% | skill 1/1; base 1/1 | +| claude-code | codonfm-finetune-002 | 728,893 | 1,887,306 | -1,158,413 | -61.38% | skill 1/1; base 1/1 | +| claude-code | codonfm-finetune-003 | 554,995 | 804,299 | -249,304 | -31.00% | skill 1/1; base 1/1 | +| codex | All cases | 1,217,895 | 1,719,144 | -501,249 | -29.16% | skill 3/3; base 3/3 | +| codex | codonfm-finetune-001 | 536,353 | 1,008,386 | -472,033 | -46.81% | skill 1/1; base 1/1 | +| codex | codonfm-finetune-002 | 435,825 | 405,824 | +30,001 | +7.39% | skill 1/1; base 1/1 | +| codex | codonfm-finetune-003 | 245,717 | 304,934 | -59,217 | -19.42% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 3,477,606 | 7,037,223 | -3,559,617 | -50.58% | skill 6/6; base 6/6 | Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. @@ -80,7 +80,7 @@ Prompt tokens include cached reads, so total tokens are `prompt + completion` (c - **MEDIUM** QUALITY/quality_correctness: No documented scripts in table format (`skills/codonfm-finetune/SKILL.md`) - **MEDIUM** QUALITY/quality_correctness: Instructions don't mention 'run_script' (`skills/codonfm-finetune/SKILL.md`) - **MEDIUM** QUALITY/quality_correctness: SKILL_SPEC recommended field missing: 'metadata.tags' (`skills/codonfm-finetune/SKILL.md`) -- **MEDIUM** SECURITY/Unknown (LP3): MCP Least Privilege: The skill declares no explicit tool scope (permissions or allowed-tools) but contains code-execution and file-write capa (`SKILL.md:1`) +- **MEDIUM** SECURITY/Unknown (LP3): MCP Least Privilege: The skill declares no explicit tool scope (no 'permissions' or 'allowed-tools' field in metadata), yet the skill content (`SKILL.md:1`) - **MEDIUM** SECURITY/Autonomous Decision Making (EA2): Excessive Agency: without checking (`SKILL.md:162`) - 11 additional finding(s) are available in the full evaluation artifacts. diff --git a/skills/codonfm-finetune/skill-card.md b/skills/codonfm-finetune/skill-card.md index 0c788dc..1dc1532 100644 --- a/skills/codonfm-finetune/skill-card.md +++ b/skills/codonfm-finetune/skill-card.md @@ -9,7 +9,7 @@ NVIDIA
### License/Terms of Use:
Apache 2.0
## Use Case:
-Developers and computational biologists fine-tune CodonFM Encodon checkpoints on labeled coding-sequence or variant data for sequence-level regression or classification.
+Developers and computational biologists use this skill to fine-tune NVIDIA CodonFM Encodon foundation models on labeled coding-sequence or coding-variant datasets for regression or classification tasks.
### Deployment Geography for Use:
Global
@@ -28,10 +28,9 @@ Mitigation: Review and scan skill before deployment.
- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
-- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
-- [CodonFM Encodon on NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NV-CodonFM-Encodon on NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [CenikLab TE Classic ML Data](https://github.com/CenikLab/TE_classic_ML/tree/main/data)
- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
-- [CenikLab/TE_classic_ML (upstream RiboNN data)](https://github.com/CenikLab/TE_classic_ML/tree/main/data)
## Skill Output:
@@ -47,39 +46,39 @@ Mitigation: Review and scan skill before deployment.
## Evaluation Tasks:
-Evaluated against 3 internal evaluation tasks (3 positive) across 2 agents, each attempt in an isolated sandbox pod.
+Evaluated against 3 positive evaluation tasks in isolated k8s-sandbox pods, covering LoRA fine-tuning preparation, variant classification, and data preparation workflows.
## Evaluation Metrics Used:
Reported benchmark dimensions:
-- Security: Is it safe to use? Checks for unsafe operations, secret leakage, and unauthorized access.
-- Correctness: Is the answer correct? Final-answer correctness against the reference answer.
-- Discoverability: Was the right skill loaded when needed? Whether the expected skill was selected, decoys were avoided, and the workflow executed.
-- Effectiveness: Did the skill help complete the task? Equal-weight mean of goal completion and expected workflow adherence.
-- Efficiency: Did it avoid wasted tool calls and token usage? 50% tool-call productivity and 50% token efficiency.
+- Security: Whether the skill is safe to use: checks for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Whether the final answer is correct against the reference answer.
+- Discoverability: Whether the right skill was loaded when needed: skill selection, decoy avoidance, and workflow execution.
+- Effectiveness: Whether the skill helped complete the task: goal completion (50%) and expected workflow adherence (50%).
+- Efficiency: Whether wasted tool calls and token usage were avoided: tool-call productivity (50%) and token efficiency (50%).
Underlying evaluation signals used in this run:
-- `security`: Unsafe operations, secret leakage, and unauthorized access.
-- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
-- `skill_efficiency`: Tool-call productivity (routing scored under Discoverability).
+- `security`: Checks for unsafe operations, secret leakage, and unauthorized access.
- `accuracy`: Final-answer correctness against the reference answer.
+- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
- `goal_accuracy`: Whether the user's goal was achieved.
- `behavior_check`: Whether the expected workflow behavior was followed.
-- `token_efficiency`: Actual uncached prompt plus completion token usage.
+- `skill_efficiency`: Tool-call productivity (legacy wire id; routing scored under Discoverability).
+- `token_efficiency`: Actual uncached prompt plus completion usage.
## Evaluation Results:
| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 89.9% | 87.9% | -| Security | 100.0% → 100.0% (±0.0 pts) | 33.3% → 66.7% (+33.4 pts) | -| Correctness | 100.0% → 100.0% (±0.0 pts) | 100.0% → 100.0% (±0.0 pts) | -| Discoverability | 87.7% | 91.0% | -| Effectiveness | 83.3% → 80.0% (-3.3 pts) | 86.7% → 93.3% (+6.6 pts) | -| Efficiency | 82.0% | 88.5% | +| Overall | 85.0% | 82.8% | +| Security | 100.0% → 66.7% (-33.3 points) | 33.3% → 66.7% (+33.4 points) | +| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | +| Discoverability | 91.7% | 76.7% | +| Effectiveness | 86.7% → 86.7% (±0.0 points) | 96.7% → 90.0% (-6.7 points) | +| Efficiency | 80.0% | 80.7% | ## Skill Version(s):
-29194e8 (source: git SHA, committed 2026-09-18)
+be43117 (source: git SHA, committed 2026-10-07)
## Ethical Considerations:
NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
diff --git a/skills/codonfm-finetune/skill.oms.sig b/skills/codonfm-finetune/skill.oms.sig index e7afea2..694aee4 100644 --- a/skills/codonfm-finetune/skill.oms.sig +++ b/skills/codonfm-finetune/skill.oms.sig @@ -1 +1 @@ -{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1maW5ldHVuZSIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI3ZjBmN2ZmYjZmMGE0MGY2ZTU0YzcwNTgxMmRlZTE1NTU5ZmM5MmZkNTU3MjEyYTRkMGEzNDUwNTI4ODk5M2IwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAic2VyaWFsaXphdGlvbiI6IHsKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UsCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIsCiAgICAgICJpZ25vcmVfcGF0aHMiOiBbCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0aHViIgogICAgICBdCiAgICB9LAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjcyMzc2NDI0ZTc4ZTJmZDZiMmJkYzA0ZjA0ZmMyMWVkMzViYTQ2YzAzM2NkODc3NDUzZmMwMWQ2NThiOGFjZWQiLAogICAgICAgICJuYW1lIjogIkJFTkNITUFSSy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjViNTVjZDkxNjRjYzVkYzI3ZTk5NjViNzg1YmM4MjdmYzdlOTQ1ZjgwZDMxZTJhMjZhNmMyN2FiYTFmYTQ2YTMiLAogICAgICAgICJuYW1lIjogIlNLSUxMLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzllY2Q5OTgyMTdjNWE3NWE5YTFlZjZiZTE3MGJkMGEzYjY2ODQ1NGM3MWQ3Y2NjZGVjYTUxYmQyYWM5MjJkYyIsCiAgICAgICAgIm5hbWUiOiAiYWdlbnRzL29wZW5haS55YW1sIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjEwYWEzOWEyNzI4OGViYzhiNTIwNTY2NjVmMGZlZmEwZTk4MmQwOWY1OThlYzUwMjAyNTM4ODE3ZDhlYmIwYiIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImYwYzEyMmYxOWEyZDMxNmZlOTVjY2ZhYTBhYzUxYmQ2MTNjZmFiZGQwOWZkODlmYmI3NjY1NjJhNWYwZWVkMjciLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2NvZG9uZm1fc291cmNlLnppcCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2VuY29kb25fY2hlY2twb2ludC5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNGMyMzUxNzk0MGIwOGYxODk2ZjMyY2FmNmYwNGU2MDMwNGQzNDNlYmY2M2YzZWZjYTY3ZjJiZjhjNTBjZjc2ZCIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvcmlib25uX3Ntb2tlLnByb3ZlbmFuY2UuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImYzODVhMTgxYTM3YzdhZDU4Y2U0N2VmNWY2OTYyMDk2ZGNiODMwNjMxMjkwYmM4NDdhNjdiODY4OTZmN2FmN2UiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL3JpYm9ubl9zbW9rZS50c3YiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmOTA0NDAwMGQ2ODFiMjVlNjdiYWI4OGJkZDk0YTUyNGI0ODQ3MDY1YjI4YzhlZDcwY2ExYTYyMWI3OWFkZWIzIiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy92YXJpYW50c19sYWJlbGVkLmNzdiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViMmVhYzI4ZTc3YzRkZWMwNzdiZTgwNjk2MGFlOGJiMzI5ZDg2ODYzM2IyMWYyYzQ5MWE2NTU1YmI1NjU3MTEiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL3ZhcmlhbnRzX2xhYmVsZWQucHJvdmVuYW5jZS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOGYwZTA0NjBlYzc1OGNjMGQzOGIzYWExMGIzZDFjYzZmNWRlNzc1ODI3NjUxMTg3NWNkMjU1MDU0ODM1ZDk1YyIsCiAgICAgICAgIm5hbWUiOiAic2NyaXB0cy9wcmVwYXJlX3JpYm9ubi5weSIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNjMjQ5Y2RkMmMzNmVhYTAzZGFkYzFkMTg0NzJiMjViZDY4MzU5MmEwNjUzZDJkNDZmMDcxOWVkYjk4YjkwOTciLAogICAgICAgICJuYW1lIjogInNraWxsLWNhcmQubWQiCiAgICAgIH0KICAgIF0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGQCMHo1dOSS6vYDVPddO5PvaSt8OgLxOPkl3M9Hsx3jQL4cd5RHEuepPMXjoXdq3k3g2QIwHlRlfue3DBNYjPgR5X9aZdfIQACrQRW0/JrnrP2gs9YZwIyQRM1bNRTrDTT74v42","keyid":""}]}} \ No newline at end of file +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1maW5ldHVuZSIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIzZjE4NGUwYjk0MzljMmE1NGM4M2YxMzAzZWFlZTkyOGE1YWY5NzAwOWFmZWIwMTBhMmI4NGQ2YmYwMzAwZjY4IgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAic2VyaWFsaXphdGlvbiI6IHsKICAgICAgImhhc2hfdHlwZSI6ICJzaGEyNTYiLAogICAgICAiaWdub3JlX3BhdGhzIjogWwogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0aHViIiwKICAgICAgICAiLmdpdCIKICAgICAgXSwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIsCiAgICAgICJhbGxvd19zeW1saW5rcyI6IGZhbHNlCiAgICB9LAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJCRU5DSE1BUksubWQiLAogICAgICAgICJkaWdlc3QiOiAiODlkN2NkNTQ1ZjhhODU4Y2EwYjZkNDgxOTlmN2YzNzMyMDMxNjJmYWYzMWYxYTk5N2Y2MzY0YWY2ZmVlZjk2MiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJTS0lMTC5tZCIsCiAgICAgICAgImRpZ2VzdCI6ICI1YjU1Y2Q5MTY0Y2M1ZGMyN2U5OTY1Yjc4NWJjODI3ZmM3ZTk0NWY4MGQzMWUyYTI2YTZjMjdhYmExZmE0NmEzIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJuYW1lIjogImFnZW50cy9vcGVuYWkueWFtbCIsCiAgICAgICAgImRpZ2VzdCI6ICI3OWVjZDk5ODIxN2M1YTc1YTlhMWVmNmJlMTcwYmQwYTNiNjY4NDU0YzcxZDdjY2NkZWNhNTFiZDJhYzkyMmRjIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJuYW1lIjogImV2YWxzL2V2YWxzLmpzb24iLAogICAgICAgICJkaWdlc3QiOiAiNjEwYWEzOWEyNzI4OGViYzhiNTIwNTY2NjVmMGZlZmEwZTk4MmQwOWY1OThlYzUwMjAyNTM4ODE3ZDhlYmIwYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy9jb2RvbmZtX3NvdXJjZS56aXAiLAogICAgICAgICJkaWdlc3QiOiAiZjBjMTIyZjE5YTJkMzE2ZmU5NWNjZmFhMGFjNTFiZDYxM2NmYWJkZDA5ZmQ4OWZiYjc2NjU2MmE1ZjBlZWQyNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy9lbmNvZG9uX2NoZWNrcG9pbnQuanNvbiIsCiAgICAgICAgImRpZ2VzdCI6ICI2OThmYTM1NDE0MDAzM2U5YWE4NTkxY2QyODYwNzAzYTRmY2RjMmViMjdlZGM4ZjJmMDc2MGM0MDgzZTU2M2VlIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL3JpYm9ubl9zbW9rZS5wcm92ZW5hbmNlLmpzb24iLAogICAgICAgICJkaWdlc3QiOiAiNGMyMzUxNzk0MGIwOGYxODk2ZjMyY2FmNmYwNGU2MDMwNGQzNDNlYmY2M2YzZWZjYTY3ZjJiZjhjNTBjZjc2ZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy9yaWJvbm5fc21va2UudHN2IiwKICAgICAgICAiZGlnZXN0IjogImYzODVhMTgxYTM3YzdhZDU4Y2U0N2VmNWY2OTYyMDk2ZGNiODMwNjMxMjkwYmM4NDdhNjdiODY4OTZmN2FmN2UiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvdmFyaWFudHNfbGFiZWxlZC5jc3YiLAogICAgICAgICJkaWdlc3QiOiAiZjkwNDQwMDBkNjgxYjI1ZTY3YmFiODhiZGQ5NGE1MjRiNDg0NzA2NWIyOGM4ZWQ3MGNhMWE2MjFiNzlhZGViMyIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy92YXJpYW50c19sYWJlbGVkLnByb3ZlbmFuY2UuanNvbiIsCiAgICAgICAgImRpZ2VzdCI6ICJlYjJlYWMyOGU3N2M0ZGVjMDc3YmU4MDY5NjBhZThiYjMyOWQ4Njg2MzNiMjFmMmM0OTFhNjU1NWJiNTY1NzExIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJuYW1lIjogInNjcmlwdHMvcHJlcGFyZV9yaWJvbm4ucHkiLAogICAgICAgICJkaWdlc3QiOiAiOGYwZTA0NjBlYzc1OGNjMGQzOGIzYWExMGIzZDFjYzZmNWRlNzc1ODI3NjUxMTg3NWNkMjU1MDU0ODM1ZDk1YyIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAibmFtZSI6ICJza2lsbC1jYXJkLm1kIiwKICAgICAgICAiZGlnZXN0IjogImQ2NGFhOTljNWU3OWQ0ODNhN2Y3OWNlMTYzM2I3ZTcwNmZjYWNlMjA1OTc3NTJhNDhkOWEwZWRjMTMyMzI0YzgiCiAgICAgIH0KICAgIF0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCMB+jQojEEjtEmNDADmaSp/3H9/y8b7x+PVgJHL+bJ77mBxXphxqesk2ZtSzUTVwRdQIxAMAFmYyJ4eKBmzCmrQk5Ybvqsfzp4Bvd/Mcp+OSgp6s/rzyGATpxUYvMjC0+0hNs0g==","keyid":""}]}} \ No newline at end of file diff --git a/skills/codonfm-score/BENCHMARK.md b/skills/codonfm-score/BENCHMARK.md index d49877a..b77891b 100644 --- a/skills/codonfm-score/BENCHMARK.md +++ b/skills/codonfm-score/BENCHMARK.md @@ -9,7 +9,7 @@ Recommended for publication based on the completed evaluation evidence in this r ## Evaluation Metadata - Skill: `codonfm-score` -- Evaluation date: 2026-10-02 +- Evaluation date: 2026-10-07 - Evaluator version: `1.5.6` - Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-5`), Codex (`openai/openai/gpt-5.5`) - Tasks: 3 evaluation tasks (2 positive, 1 negative) @@ -35,12 +35,12 @@ The three-tier evaluation checks whether the skill: | Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 88.8% — baseline ran, but no comparable score was available; uplift unavailable | 91.6% — baseline ran, but no comparable score was available; uplift unavailable | -| Security | 100.0% → 66.7% (-33.3 points) | 66.7% → 100.0% (+33.3 points) | +| Overall | 96.1% — baseline ran, but no comparable score was available; uplift unavailable | 84.8% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 100.0% → 100.0% (±0.0 points) | 33.3% → 66.7% (+33.4 points) | | Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | -| Discoverability | 97.5% — baseline ran, but no comparable score was available; uplift unavailable | 75.0% — baseline ran, but no comparable score was available; uplift unavailable | -| Effectiveness | 96.7% → 90.0% (-6.7 points) | 93.3% → 93.3% (±0.0 points) | -| Efficiency | 90.0% — baseline ran, but no comparable score was available; uplift unavailable | 89.6% — baseline ran, but no comparable score was available; uplift unavailable | +| Discoverability | 95.0% — baseline ran, but no comparable score was available; uplift unavailable | 80.0% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 90.0% → 93.3% (+3.3 points) | 90.0% → 96.7% (+6.7 points) | +| Efficiency | 92.1% — baseline ran, but no comparable score was available; uplift unavailable | 80.7% — baseline ran, but no comparable score was available; uplift unavailable | **How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. @@ -54,15 +54,15 @@ Actual Tier 3 execution usage is reported for every observed agent/case pair and | Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | |---|---|---:|---:|---:|---:|---| -| claude-code | All cases | 1,540,091 | 2,144,581 | -604,490 | -28.19% | skill 3/3; base 3/3 | -| claude-code | codonfm-score-001 | 713,780 | 1,298,851 | -585,071 | -45.05% | skill 1/1; base 1/1 | -| claude-code | codonfm-score-002 | 672,716 | 726,625 | -53,909 | -7.42% | skill 1/1; base 1/1 | -| claude-code | codonfm-score-003 | 153,595 | 119,105 | +34,490 | +28.96% | skill 1/1; base 1/1 | -| codex | All cases | 921,033 | 1,390,032 | -468,999 | -33.74% | skill 3/3; base 3/3 | -| codex | codonfm-score-001 | 390,238 | 816,264 | -426,026 | -52.19% | skill 1/1; base 1/1 | -| codex | codonfm-score-002 | 517,306 | 518,092 | -786 | -0.15% | skill 1/1; base 1/1 | -| codex | codonfm-score-003 | 13,489 | 55,676 | -42,187 | -75.77% | skill 1/1; base 1/1 | -| ALL AGENTS | Dataset aggregate | 2,461,124 | 3,534,613 | -1,073,489 | -30.37% | skill 6/6; base 6/6 | +| claude-code | All cases | 1,308,984 | 3,855,619 | -2,546,635 | -66.05% | skill 3/3; base 3/3 | +| claude-code | codonfm-score-001 | 677,690 | 2,757,266 | -2,079,576 | -75.42% | skill 1/1; base 1/1 | +| claude-code | codonfm-score-002 | 457,608 | 884,998 | -427,390 | -48.29% | skill 1/1; base 1/1 | +| claude-code | codonfm-score-003 | 173,686 | 213,355 | -39,669 | -18.59% | skill 1/1; base 1/1 | +| codex | All cases | 1,491,127 | 1,204,536 | +286,591 | +23.79% | skill 3/3; base 3/3 | +| codex | codonfm-score-001 | 477,556 | 566,528 | -88,972 | -15.70% | skill 1/1; base 1/1 | +| codex | codonfm-score-002 | 1,000,069 | 624,731 | +375,338 | +60.08% | skill 1/1; base 1/1 | +| codex | codonfm-score-003 | 13,502 | 13,277 | +225 | +1.69% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 2,800,111 | 5,060,155 | -2,260,044 | -44.66% | skill 6/6; base 6/6 | Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. diff --git a/skills/codonfm-score/skill-card.md b/skills/codonfm-score/skill-card.md index c438315..e45d748 100644 --- a/skills/codonfm-score/skill-card.md +++ b/skills/codonfm-score/skill-card.md @@ -9,7 +9,7 @@ NVIDIA
### License/Terms of Use:
Apache 2.0
## Use Case:
-Developers and bioinformatics researchers use this skill to validate, prepare, and execute masked-codon variant scoring with public CodonFM Encodon models for codon-level genomic analysis.
+Developers and computational biologists use this skill to validate variant CSV inputs and run or prepare masked-codon scoring commands with public Encodon models.
### Deployment Geography for Use:
Global
@@ -28,16 +28,15 @@ Mitigation: Review and scan skill before deployment.
- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
-- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
-- [CodonFM Encodon (NGC Catalog)](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
-- [NVIDIA Drug and Biomolecular Research](https://research.nvidia.com/labs/dbr)
+- [NV CodonFM Encodon (NGC Catalog)](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
## Skill Output:
-**Output Type(s):** [Shell commands, Analysis, Configuration instructions]
+**Output Type(s):** [Shell commands, Analysis]
**Output Format:** [Markdown with inline bash code blocks]
**Output Parameters:** [1D]
-**Other Properties Related to Output:** [None]
+**Other Properties Related to Output:** [Produces NumPy arrays (ref_likelihoods, alt_likelihoods, likelihood_ratios, ids) when inference executes]
## Evaluation Agents Used:
- Claude Code (`aws/anthropic/bedrock-claude-opus-5`)
@@ -46,39 +45,39 @@ Mitigation: Review and scan skill before deployment.
## Evaluation Tasks:
-3 evaluation tasks (2 positive, 1 negative) run in isolated sandbox pods, evaluator version 1.5.6.
+3 evaluation tasks (2 positive, 1 negative) in isolated sandbox pods.
## Evaluation Metrics Used:
Reported benchmark dimensions:
- Security: Checks for unsafe operations, secret leakage, and unauthorized access.
-- Correctness: Final-answer correctness against the reference answer.
-- Discoverability: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
-- Effectiveness: Equal-weight mean of goal completion (goal_accuracy) and expected workflow adherence (behavior_check).
-- Efficiency: 50% tool-call productivity and 50% token efficiency.
+- Correctness: Checks final-answer correctness against the reference answer.
+- Discoverability: Checks whether the expected skill was selected, decoys were avoided, and the workflow executed.
+- Effectiveness: Checks whether the skill helped complete the user's goal and followed the expected workflow behavior.
+- Efficiency: Checks tool-call productivity and token efficiency to detect wasted skill and tool usage.
Underlying evaluation signals used in this run:
- `security`: Unsafe operations, secret leakage, and unauthorized access.
- `accuracy`: Final-answer correctness against the reference answer.
-- `skill_execution`: Whether the expected skill was selected and the workflow executed.
+- `skill_execution`: Whether the expected skill was selected, decoys were avoided, and the workflow executed.
- `goal_accuracy`: Whether the user's goal was achieved.
- `behavior_check`: Whether the expected workflow behavior was followed.
-- `skill_efficiency`: Tool-call productivity (routing scored under Discoverability).
-- `token_efficiency`: Actual uncached prompt plus completion token usage.
+- `skill_efficiency`: Tool-call productivity (legacy wire id; routing is scored under Discoverability).
+- `token_efficiency`: Actual uncached prompt plus completion usage (50% of Efficiency).
## Evaluation Results:
| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 88.8% | 91.6% | -| Security | 100.0% → 66.7% (-33.3 points) | 66.7% → 100.0% (+33.3 points) | -| Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | -| Discoverability | 97.5% | 75.0% | -| Effectiveness | 96.7% → 90.0% (-6.7 points) | 93.3% → 93.3% (±0.0 points) | -| Efficiency | 90.0% | 89.6% | +| Overall | 96.1% | 84.8% | +| Security | 100.0% → 100.0% (±0.0 pts) | 33.3% → 66.7% (+33.4 pts) | +| Correctness | 100.0% → 100.0% (±0.0 pts) | 100.0% → 100.0% (±0.0 pts) | +| Discoverability | 95.0% | 80.0% | +| Effectiveness | 90.0% → 93.3% (+3.3 pts) | 90.0% → 96.7% (+6.7 pts) | +| Efficiency | 92.1% | 80.7% | ## Skill Version(s):
-29194e8 (source: git SHA, committed 2026-09-18)
+be43117 (source: git SHA, committed 2026-10-07)
## Ethical Considerations:
NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
diff --git a/skills/codonfm-score/skill.oms.sig b/skills/codonfm-score/skill.oms.sig index 24928d7..35a6a5c 100644 --- a/skills/codonfm-score/skill.oms.sig +++ b/skills/codonfm-score/skill.oms.sig @@ -1 +1 @@ -{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1zY29yZSIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIxNjM5MDg3YzFjZTQyYjAxYzk0YmJjZTVlM2M4YTg3YWY4MGM4OWI3Yjg1ZjU0MDIxMDU5Njg1ODk1YmJkNzEzIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICI2M2QwZTEzMWE0ODQ2YjRlMGM2NzhmNTIyOTZkYzgyNzlmZDkyZjIyMDAzMTQwNWVhNWQwNTc1NTU5NTc4OTJhIiwKICAgICAgICAibmFtZSI6ICJCRU5DSE1BUksubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICIxOGY3NjYyYTUzYTg2YWM1ZmM2OTUyNjFlODFkMTViOTA0ZmUzMmQ1NzQwZGM1N2IyZWVmZmQ3MWM0MGEzNTZmIiwKICAgICAgICAibmFtZSI6ICJTS0lMTC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogImFjM2JjNWU1YzdjMzhjMjg0MjczYzY4MTgzNDI2YWE0OWY5ZjMzZmVkOGQzYTU4ZTNjZjIxYmFiOGZiZjUyYTYiLAogICAgICAgICJuYW1lIjogImFnZW50cy9vcGVuYWkueWFtbCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogImQwMGFhYmMzNTQ2MzZiODk2NzI5NWM3ZmM4YTk5ZjJlMGIzMjMyODViNzdlODRlNjU2YjQ5ODBhNzBlODUyNjUiLAogICAgICAgICJuYW1lIjogImV2YWxzL2NvbmZpZy55bWwiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICJkOWU4N2FmZmIyMTZmZjg3YjlmNGIwMTkxYTcwOWUyMDM5NzJmMWY5ZTk0NTZkYmRkYTNmNjE1MWYyMWZjMTM3IiwKICAgICAgICAibmFtZSI6ICJldmFscy9ldmFscy5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiZjBjMTIyZjE5YTJkMzE2ZmU5NWNjZmFhMGFjNTFiZDYxM2NmYWJkZDA5ZmQ4OWZiYjc2NjU2MmE1ZjBlZWQyNyIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvY29kb25mbV9zb3VyY2UuemlwIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiNjk4ZmEzNTQxNDAwMzNlOWFhODU5MWNkMjg2MDcwM2E0ZmNkYzJlYjI3ZWRjOGYyZjA3NjBjNDA4M2U1NjNlZSIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvZW5jb2Rvbl9jaGVja3BvaW50Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICJlMjhiNjMzMGRiMDA3NTkxZWNkZGUyMTU1YTJmMTE4ZjQxZTIzNjk5NzNiNWI4MGQ1NmE2OTg0NmEyMGQ1NjBkIiwKICAgICAgICAibmFtZSI6ICJldmFscy9maWxlcy92YXJpYW50cy5jc3YiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICI0YTUzNzk5NDY4MjExNTczYWU2YWI4ZWI4NTU4ZWQ2OGM2OGFkOTM0ZDkxM2I0MThhMmE4YWZlZGNkMjBiNDhmIiwKICAgICAgICAibmFtZSI6ICJza2lsbC1jYXJkLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAiaGFzaF90eXBlIjogInNoYTI1NiIsCiAgICAgICJpZ25vcmVfcGF0aHMiOiBbCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0aHViIgogICAgICBdLAogICAgICAibWV0aG9kIjogImZpbGVzIiwKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGYCMQCxOlmeztfHY/2TBXcn849LYs9Lq7uX5XKoFU34SjdY3SFvi0J55R1nWl2DNstVjlACMQCYzrVGNxO/i2Bp2EoQ58CGGDnNCoMn3tv+ooe9cVb32vQ6aM3Df4xK8ZdhgFCqm7Q=","keyid":""}]}} \ No newline at end of file +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1zY29yZSIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI3NDdhY2EwMjJjYzE2Mzk5ODY0N2Y5NjE1MzFjN2JhZGUwMmE0NjQ3Njk4NGFhMTZlZDk3OGExOTBlNjU1ZWFiIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAic2VyaWFsaXphdGlvbiI6IHsKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UsCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIsCiAgICAgICJpZ25vcmVfcGF0aHMiOiBbCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0aWdub3JlIiwKICAgICAgICAiLmdpdGh1YiIsCiAgICAgICAgIi5naXRhdHRyaWJ1dGVzIgogICAgICBdCiAgICB9LAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiMTgxNDdiMWFiYzNhMDNiZjVhZWQ1YjA3ZTA1ZTAzZGM1ODJmMWQxZDM1MDM2ODg0NmZlYjA2MzQxY2E0MGFmZSIsCiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiMThmNzY2MmE1M2E4NmFjNWZjNjk1MjYxZTgxZDE1YjkwNGZlMzJkNTc0MGRjNTdiMmVlZmZkNzFjNDBhMzU2ZiIsCiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICJhYzNiYzVlNWM3YzM4YzI4NDI3M2M2ODE4MzQyNmFhNDlmOWYzM2ZlZDhkM2E1OGUzY2YyMWJhYjhmYmY1MmE2IiwKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgImRpZ2VzdCI6ICJkMDBhYWJjMzU0NjM2Yjg5NjcyOTVjN2ZjOGE5OWYyZTBiMzIzMjg1Yjc3ZTg0ZTY1NmI0OTgwYTcwZTg1MjY1IiwKICAgICAgICAibmFtZSI6ICJldmFscy9jb25maWcueW1sIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiZDllODdhZmZiMjE2ZmY4N2I5ZjRiMDE5MWE3MDllMjAzOTcyZjFmOWU5NDU2ZGJkZGEzZjYxNTFmMjFmYzEzNyIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogImYwYzEyMmYxOWEyZDMxNmZlOTVjY2ZhYTBhYzUxYmQ2MTNjZmFiZGQwOWZkODlmYmI3NjY1NjJhNWYwZWVkMjciLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2NvZG9uZm1fc291cmNlLnppcCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiLAogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2VuY29kb25fY2hlY2twb2ludC5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiZTI4YjYzMzBkYjAwNzU5MWVjZGRlMjE1NWEyZjExOGY0MWUyMzY5OTczYjViODBkNTZhNjk4NDZhMjBkNTYwZCIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZmlsZXMvdmFyaWFudHMuY3N2IiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJkaWdlc3QiOiAiMGQzNTA3YjhmZDU2MWVhY2QyNDAwMDRjZmY0NzBlYjIxZDFmOGJlZmM0YjUxY2FhM2RlY2FiZjJmM2ZjYmJjOSIsCiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0KICAgIF0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGQCMFRcNJbnOy3MCAqDWGV9yEJESKsSC9nFXZ/u3srx2WxqeqRiFye0LeNECTI2POnXSwIwM4GlYHdPzJ4AUlo7qbgQqqDWd7Ex70Ru5YRxQl9l6J4aVNQlcwvpragN/Vc6pc2f","keyid":""}]}} \ No newline at end of file diff --git a/skills/codonfm-setup/BENCHMARK.md b/skills/codonfm-setup/BENCHMARK.md index d8f2f45..4714257 100644 --- a/skills/codonfm-setup/BENCHMARK.md +++ b/skills/codonfm-setup/BENCHMARK.md @@ -9,7 +9,7 @@ Recommended for publication based on the completed evaluation evidence in this r ## Evaluation Metadata - Skill: `codonfm-setup` -- Evaluation date: 2026-10-02 +- Evaluation date: 2026-10-07 - Evaluator version: `1.5.6` - Agents: Claude Code (`aws/anthropic/bedrock-claude-opus-5`), Codex (`openai/openai/gpt-5.5`) - Tasks: 3 evaluation tasks (3 positive) @@ -35,12 +35,12 @@ The three-tier evaluation checks whether the skill: | Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | |---|---:|---:| -| Overall | 93.6% — baseline ran, but no comparable score was available; uplift unavailable | 95.7% — baseline ran, but no comparable score was available; uplift unavailable | -| Security | 66.7% → 100.0% (+33.3 points) | 66.7% → 100.0% (+33.3 points) | +| Overall | 92.8% — baseline ran, but no comparable score was available; uplift unavailable | 94.8% — baseline ran, but no comparable score was available; uplift unavailable | +| Security | 100.0% → 100.0% (±0.0 points) | 0.0% → 100.0% (+100.0 points) | | Correctness | 100.0% → 100.0% (±0.0 points) | 100.0% → 100.0% (±0.0 points) | -| Discoverability | 95.0% — baseline ran, but no comparable score was available; uplift unavailable | 90.0% — baseline ran, but no comparable score was available; uplift unavailable | -| Effectiveness | 83.3% → 87.5% (+4.2 points) | 85.8% → 95.0% (+9.2 points) | -| Efficiency | 85.6% — baseline ran, but no comparable score was available; uplift unavailable | 93.5% — baseline ran, but no comparable score was available; uplift unavailable | +| Discoverability | 100.0% — baseline ran, but no comparable score was available; uplift unavailable | 88.3% — baseline ran, but no comparable score was available; uplift unavailable | +| Effectiveness | 87.5% → 75.0% (-12.5 points) | 85.8% → 91.7% (+5.9 points) | +| Efficiency | 89.2% — baseline ran, but no comparable score was available; uplift unavailable | 94.0% — baseline ran, but no comparable score was available; uplift unavailable | **How to read this table:** baseline is the same task attempted without the target skill. Scores are rounded to one decimal; threshold-adjacent values use additional precision so their displayed band matches the verdict. Uplift is derived from those displayed scores and shown in percentage points. @@ -52,15 +52,15 @@ Actual Tier 3 execution usage is reported for every observed agent/case pair and | Agent | Dataset case | With skill | Without skill | Delta | Change | Coverage | |---|---|---:|---:|---:|---:|---| -| claude-code | All cases | 2,462,441 | 5,734,843 | -3,272,402 | -57.06% | skill 3/3; base 3/3 | -| claude-code | codonfm-setup-001 | 393,909 | 1,408,765 | -1,014,856 | -72.04% | skill 1/1; base 1/1 | -| claude-code | codonfm-setup-002 | 537,459 | 672,118 | -134,659 | -20.04% | skill 1/1; base 1/1 | -| claude-code | codonfm-setup-003 | 1,531,073 | 3,653,960 | -2,122,887 | -58.10% | skill 1/1; base 1/1 | -| codex | All cases | 676,905 | 1,297,601 | -620,696 | -47.83% | skill 3/3; base 3/3 | -| codex | codonfm-setup-001 | 161,662 | 220,831 | -59,169 | -26.79% | skill 1/1; base 1/1 | -| codex | codonfm-setup-002 | 339,512 | 563,013 | -223,501 | -39.70% | skill 1/1; base 1/1 | -| codex | codonfm-setup-003 | 175,731 | 513,757 | -338,026 | -65.79% | skill 1/1; base 1/1 | -| ALL AGENTS | Dataset aggregate | 3,139,346 | 7,032,444 | -3,893,098 | -55.36% | skill 6/6; base 6/6 | +| claude-code | All cases | 1,456,315 | 3,711,392 | -2,255,077 | -60.76% | skill 3/3; base 3/3 | +| claude-code | codonfm-setup-001 | 413,540 | 665,232 | -251,692 | -37.84% | skill 1/1; base 1/1 | +| claude-code | codonfm-setup-002 | 511,909 | 542,322 | -30,413 | -5.61% | skill 1/1; base 1/1 | +| claude-code | codonfm-setup-003 | 530,866 | 2,503,838 | -1,972,972 | -78.80% | skill 1/1; base 1/1 | +| codex | All cases | 601,083 | 1,236,238 | -635,155 | -51.38% | skill 3/3; base 3/3 | +| codex | codonfm-setup-001 | 86,689 | 221,903 | -135,214 | -60.93% | skill 1/1; base 1/1 | +| codex | codonfm-setup-002 | 340,006 | 494,727 | -154,721 | -31.27% | skill 1/1; base 1/1 | +| codex | codonfm-setup-003 | 174,388 | 519,608 | -345,220 | -66.44% | skill 1/1; base 1/1 | +| ALL AGENTS | Dataset aggregate | 2,057,398 | 4,947,630 | -2,890,232 | -58.42% | skill 6/6; base 6/6 | Prompt tokens include cached reads, so total tokens are `prompt + completion` (cached is not added twice). The Efficiency score uses `(prompt - cached) + completion`. N/A means the relevant trajectory counters were not available; coverage is never estimated. diff --git a/skills/codonfm-setup/skill-card.md b/skills/codonfm-setup/skill-card.md index 2ccaa13..8d7021d 100644 --- a/skills/codonfm-setup/skill-card.md +++ b/skills/codonfm-setup/skill-card.md @@ -9,7 +9,7 @@ NVIDIA
### License/Terms of Use:
Apache 2.0
## Use Case:
-Developers and engineers setting up the CodonFM development environment, building or launching the development container, configuring data and checkpoint mounts, verifying GPU access, and downloading public Encodon model weights.
+Developers and computational biologists who need to set up the CodonFM development environment, build or launch the development container, configure local data and checkpoint mounts, verify GPU access, or download public Encodon 80M, 600M, 1B, or Cdwt-1B weights.
### Deployment Geography for Use:
Global
@@ -25,13 +25,15 @@ Risk: Review before execution as proposals could introduce incorrect or misleadi Mitigation: Review and scan skill before deployment.
## Reference(s):
-- [NV-CodonFM-Encodon-80M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
-- [NV-CodonFM-Encodon-600M-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
-- [NV-CodonFM-Encodon-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
-- [NV-CodonFM-Encodon-Cdwt-1B-v1 (Hugging Face)](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
-- [NV CodonFM Encodon (NGC Catalog)](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [NV-CodonFM-Encodon-80M-v1](https://huggingface.co/nvidia/NV-CodonFM-Encodon-80M-v1)
+- [NV-CodonFM-Encodon-600M-v1](https://huggingface.co/nvidia/NV-CodonFM-Encodon-600M-v1)
+- [NV-CodonFM-Encodon-1B-v1](https://huggingface.co/nvidia/NV-CodonFM-Encodon-1B-v1)
+- [NV-CodonFM-Encodon-Cdwt-1B-v1](https://huggingface.co/nvidia/NV-CodonFM-Encodon-Cdwt-1B-v1)
+- [NGC CodonFM Encodon Catalog](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/clara/models/nv_codonfm_encodon)
+- [HuggingFace Hub CLI Documentation](https://huggingface.co/docs/huggingface_hub/en/guides/cli)
- [NVIDIA CUDA Compatibility](https://docs.nvidia.com/deploy/cuda-compatibility/minor-version-compatibility.html)
-- [NVIDIA Deep Bio Research](https://research.nvidia.com/labs/dbr)
+- [PyTorch 24.10 Release Notes](https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes/rel-24-10.html#driver-requirements)
+- [NVIDIA Deep Biology Research](https://research.nvidia.com/labs/dbr)
## Skill Output:
@@ -47,39 +49,39 @@ Mitigation: Review and scan skill before deployment.
## Evaluation Tasks:
-Evaluated against 3 internal evaluation tasks (3 positive) in isolated k8s-sandbox pods with 1 attempt per task.
+3 evaluation tasks (3 positive) from a curated dataset, each run in an isolated sandbox pod.
## Evaluation Metrics Used:
Reported benchmark dimensions:
-- Security: Checks for unsafe operations, secret leakage, and unauthorized access.
-- Correctness: Checks final-answer correctness against the reference answer.
-- Discoverability: Checks whether the expected skill was selected, decoys were avoided, and the workflow executed.
-- Effectiveness: Checks whether the user's goal was achieved and expected workflow behavior was followed.
-- Efficiency: Checks tool-call productivity and token efficiency.
+- Security: Whether the skill is safe to use, checking for unsafe operations, secret leakage, and unauthorized access.
+- Correctness: Whether the final answer is correct against the reference answer.
+- Discoverability: Whether the right skill was loaded when needed, including skill selection and decoy avoidance.
+- Effectiveness: Whether the skill helped complete the user's goal (50% goal accuracy + 50% expected workflow adherence).
+- Efficiency: Whether the skill avoided wasted tool calls and token usage (50% tool-call productivity + 50% token efficiency).
Underlying evaluation signals used in this run:
-- `security`: Unsafe operations, secret leakage, and unauthorized access.
+- `security`: Checks for unsafe operations, secret leakage, and unauthorized access.
- `accuracy`: Final-answer correctness against the reference answer.
- `skill_execution`: Whether the expected skill was selected and the workflow executed.
- `goal_accuracy`: Whether the user's goal was achieved.
- `behavior_check`: Whether the expected workflow behavior was followed.
-- `skill_efficiency`: Tool-call productivity.
-- `token_efficiency`: Actual uncached prompt plus completion token usage.
+- `skill_efficiency`: Tool-call productivity; routing is scored under Discoverability.
+- `token_efficiency`: Actual uncached prompt plus completion usage.
## Evaluation Results:
-| Measure | Claude Code (Baseline → Skill Uplift) | Codex (Baseline → Skill Uplift) | +| Measure | Claude Code | Codex | |---|---:|---:| -| Overall | 93.6% | 95.7% | -| Security | 66.7% → 100.0% (+33.3 pts) | 66.7% → 100.0% (+33.3 pts) | -| Correctness | 100.0% → 100.0% (±0.0 pts) | 100.0% → 100.0% (±0.0 pts) | -| Discoverability | 95.0% | 90.0% | -| Effectiveness | 83.3% → 87.5% (+4.2 pts) | 85.8% → 95.0% (+9.2 pts) | -| Efficiency | 85.6% | 93.5% | +| Overall | 92.8% | 94.8% | +| Security | 100.0% | 100.0% | +| Correctness | 100.0% | 100.0% | +| Discoverability | 100.0% | 88.3% | +| Effectiveness | 75.0% | 91.7% | +| Efficiency | 89.2% | 94.0% | ## Skill Version(s):
-29194e8 (source: git SHA, committed 2026-09-18)
+be43117 (source: git SHA, committed 2026-10-07)
## Ethical Considerations:
NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
diff --git a/skills/codonfm-setup/skill.oms.sig b/skills/codonfm-setup/skill.oms.sig index 29d33d8..5526194 100644 --- a/skills/codonfm-setup/skill.oms.sig +++ b/skills/codonfm-setup/skill.oms.sig @@ -1 +1 @@ -{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1zZXR1cCIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICI0ZTczMGJkOTk2ZWJjYTJiOGI3ZTZmZWU4OWY3Y2M4MWMzM2Q2ODMxZDVlN2IyMjFkMThjZDM0YmM4YzdhZDhmIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIiwKICAgICAgICAiZGlnZXN0IjogImY4ZDA4ZjZhZTVhODIxNzhlYjFkM2I1NWFkYjcwY2QwZDE5ZjYyYzE0NGE3NmUzNDEzOTZhMWY2MTBjNTU2MGMiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiLAogICAgICAgICJkaWdlc3QiOiAiM2ZlZmNiOWRkMmM3ODk3YzQzYTkwMjllZWEwM2ViM2FjMWVlZWI5MGZjMDhhOTNlNDQ1ODYwOWUxOGY1OTcyYiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiLAogICAgICAgICJkaWdlc3QiOiAiY2I3ZmZkYWI5Yjk2OWQxNWE3OWU3ODk1MWRmNDEzM2NlNzQzMGI4ZTBjZTgwMjNkZThhMjE0MDY2YzQxNmE1ZiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9jb25maWcueW1sIiwKICAgICAgICAiZGlnZXN0IjogImQwMGFhYmMzNTQ2MzZiODk2NzI5NWM3ZmM4YTk5ZjJlMGIzMjMyODViNzdlODRlNjU2YjQ5ODBhNzBlODUyNjUiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIsCiAgICAgICAgImRpZ2VzdCI6ICIxYTA5Y2Y3ZWJhMmY2ZjcyZTg3NzdhNzkzYmRkYWNlMGNhNWJhODFmZmJhNDcxZGM0MmE3ZjJkMGE0Yzg5OGZjIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2NvZG9uZm1fc291cmNlLnppcCIsCiAgICAgICAgImRpZ2VzdCI6ICJmMGMxMjJmMTlhMmQzMTZmZTk1Y2NmYWEwYWM1MWJkNjEzY2ZhYmRkMDlmZDg5ZmJiNzY2NTYyYTVmMGVlZDI3IiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2VuY29kb25fY2hlY2twb2ludC5qc29uIiwKICAgICAgICAiZGlnZXN0IjogIjY5OGZhMzU0MTQwMDMzZTlhYTg1OTFjZDI4NjA3MDNhNGZjZGMyZWIyN2VkYzhmMmYwNzYwYzQwODNlNTYzZWUiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIsCiAgICAgICAgImRpZ2VzdCI6ICJlNGNjNWEwNzM0NTFmYTEwMjNlNzQyZDM0Y2U1YjE0Y2U1YjNhNDk2Yzc5ODRlMDdlNDY1ZGI0N2ZiMDkxNmJmIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAiYWxsb3dfc3ltbGlua3MiOiBmYWxzZSwKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGlnbm9yZSIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXRodWIiCiAgICAgIF0sCiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGYCMQDEyDQIMbFnBevznX9NvtabpfzLcch7B2WYR6+fkeD6sVOAKXp+v1rLxhmW7g1lcWACMQDUINCJ3uR+dTt+WICoRJ8qwWPWKNgHPuoR06fnbhgyA+LBCLRPAPu/06RM4Yg78p0=","keyid":""}]}} \ No newline at end of file +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiY29kb25mbS1zZXR1cCIsCiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIxNTQwODA5MDY4ZmY3OTBlZWQ2MjMxMDJjZDQ2MTg4NzE0OTk1Y2RmNGZiNTViNmE1YWY4MTEzOTU5YTU3YTJmIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL21vZGVsX3NpZ25pbmcvc2lnbmF0dXJlL3YxLjAiLAogICJwcmVkaWNhdGUiOiB7CiAgICAicmVzb3VyY2VzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiQkVOQ0hNQVJLLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NzMzNmVlZTAxZWM2OTcwNDJkNGExYjg4ODUwYTQ5OWIyZTU2MDA1YzQwYTE1OGIyY2VjZTIzMzk0YTM0NGU4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiU0tJTEwubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjNmZWZjYjlkZDJjNzg5N2M0M2E5MDI5ZWVhMDNlYjNhYzFlZWViOTBmYzA4YTkzZTQ0NTg2MDllMThmNTk3MmIiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJhZ2VudHMvb3BlbmFpLnlhbWwiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNiN2ZmZGFiOWI5NjlkMTVhNzllNzg5NTFkZjQxMzNjZTc0MzBiOGUwY2U4MDIzZGU4YTIxNDA2NmM0MTZhNWYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJldmFscy9jb25maWcueW1sIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkMDBhYWJjMzU0NjM2Yjg5NjcyOTVjN2ZjOGE5OWYyZTBiMzIzMjg1Yjc3ZTg0ZTY1NmI0OTgwYTcwZTg1MjY1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWEwOWNmN2ViYTJmNmY3MmU4Nzc3YTc5M2JkZGFjZTBjYTViYTgxZmZiYTQ3MWRjNDJhN2YyZDBhNGM4OThmYyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2NvZG9uZm1fc291cmNlLnppcCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZjBjMTIyZjE5YTJkMzE2ZmU5NWNjZmFhMGFjNTFiZDYxM2NmYWJkZDA5ZmQ4OWZiYjc2NjU2MmE1ZjBlZWQyNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2ZpbGVzL2VuY29kb25fY2hlY2twb2ludC5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2OThmYTM1NDE0MDAzM2U5YWE4NTkxY2QyODYwNzAzYTRmY2RjMmViMjdlZGM4ZjJmMDc2MGM0MDgzZTU2M2VlIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAic2tpbGwtY2FyZC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTBmNzE0ZDk4YjZjYTQ5YjBhN2ViNWE1MzRmNDY0OTVkMTZlN2Y4MzQ5Mzg2NTQ3ZjBmOTY3Nzc3N2EzMTBiMiIKICAgICAgfQogICAgXSwKICAgICJzZXJpYWxpemF0aW9uIjogewogICAgICAiYWxsb3dfc3ltbGlua3MiOiBmYWxzZSwKICAgICAgImhhc2hfdHlwZSI6ICJzaGEyNTYiLAogICAgICAiaWdub3JlX3BhdGhzIjogWwogICAgICAgICIuZ2l0YXR0cmlidXRlcyIsCiAgICAgICAgIi5naXQiLAogICAgICAgICIuZ2l0aHViIiwKICAgICAgICAiLmdpdGlnbm9yZSIKICAgICAgXSwKICAgICAgIm1ldGhvZCI6ICJmaWxlcyIKICAgIH0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCMQDg2AITMO433iKwbnuYdZfe6fGixctjWrS6sYN0Q/kTA+gVNq1Hb3A1klwgJyXzSUsCMFosYIeR1p/ROBYX2JzwPdu0cUYlCACz5S+ABYegL9L5IgFnNdXdG7Py/uS9cySYHg==","keyid":""}]}} \ No newline at end of file