From 0eb27b3781ac6ddad53bc3d10a4aa0d391aec233 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 24 Oct 2026 14:18:38 +0000 Subject: [PATCH 1/9] Add LaTeX Application Note skeleton for FastaGuard paper prep Introduce paper/ with a Bioinformatics-oriented draft (main.tex, BibTeX, Makefile, figure/table placeholders, build README) and ignore LaTeX build artifacts. Paper-prep only; no product changes. Co-authored-by: ehsan.estaji Signed-off-by: Cursor Agent --- .gitignore | 14 ++++ paper/Makefile | 27 +++++++ paper/README.md | 83 +++++++++++++++++++ paper/figures/README.md | 17 ++++ paper/main.tex | 173 ++++++++++++++++++++++++++++++++++++++++ paper/references.bib | 80 +++++++++++++++++++ paper/tables/README.md | 14 ++++ 7 files changed, 408 insertions(+) create mode 100644 paper/Makefile create mode 100644 paper/README.md create mode 100644 paper/figures/README.md create mode 100644 paper/main.tex create mode 100644 paper/references.bib create mode 100644 paper/tables/README.md diff --git a/.gitignore b/.gitignore index f6650df..60c2626 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,17 @@ __pycache__/ /.nextflow.log* /.snakemake/ /work/ + +# LaTeX build artifacts (keep paper/*.tex, *.bib, Makefile, README) +paper/*.aux +paper/*.bbl +paper/*.blg +paper/*.fdb_latexmk +paper/*.fls +paper/*.log +paper/*.out +paper/*.synctex.gz +paper/*.toc +paper/*.lof +paper/*.lot +paper/*.pdf diff --git a/paper/Makefile b/paper/Makefile new file mode 100644 index 0000000..cbd80c2 --- /dev/null +++ b/paper/Makefile @@ -0,0 +1,27 @@ +# Build the FastaGuard Application Note PDF with a standard TeX toolchain. +MAIN := main +LATEXMK ?= latexmk + +.PHONY: all pdf clean distclean view + +all: pdf + +pdf: + $(LATEXMK) -pdf -interaction=nonstopmode -halt-on-error $(MAIN).tex + +# Fallback if latexmk is unavailable: +pdflatex-only: + pdflatex -interaction=nonstopmode $(MAIN).tex + bibtex $(MAIN) + pdflatex -interaction=nonstopmode $(MAIN).tex + pdflatex -interaction=nonstopmode $(MAIN).tex + +clean: + $(LATEXMK) -c $(MAIN).tex || true + rm -f $(MAIN).bbl $(MAIN).blg $(MAIN).synctex.gz + +distclean: clean + rm -f $(MAIN).pdf + +view: pdf + @echo "Built $(MAIN).pdf" diff --git a/paper/README.md b/paper/README.md new file mode 100644 index 0000000..1d9db85 --- /dev/null +++ b/paper/README.md @@ -0,0 +1,83 @@ +# FastaGuard paper (LaTeX) + +Draft manuscript targeting **Bioinformatics (Oxford Academic) — Application Note**. + +This folder is paper preparation only. It does not change the FastaGuard product. + +## Target venue (primary) + +| Item | Choice | +| --- | --- | +| Journal | [*Bioinformatics*](https://academic.oup.com/bioinformatics) (OUP) | +| Article type | Application Note | +| Length budget | Up to ~4 pages (~2,600 words, or ~2,000 words + one figure) | +| Abstract shape | Summary / Availability and Implementation / Contact / Supplementary information | +| Software rules | Freely available to non-commercial users; state availability + implementation; keep available ≥2 years after publication | + +Format-free initial submission is allowed. For revision / camera-ready, switch to the [OUP General Template](https://www.overleaf.com/latex/templates/oup-general-template/fqkhysbcbpwv) with Bioinformatics options (`numsec,webpdf,modern,large`) and author-date bibliography style as documented by OUP. + +Venue ranking and framing notes for authors live in the Project store doc `docs/publication-venues.md` (not in this repo path). + +## Layout of this folder + +```text +paper/ + main.tex Application Note skeleton (claims + section outline) + references.bib BibTeX database + Makefile latexmk / pdflatex build + README.md this file + figures/ figure placeholders (add PDF/PNG before submission) + tables/ optional table source / CSV exports +``` + +## Build the PDF + +Requires a standard TeX Live (or MacTeX / MiKTeX) install with: + +- `pdflatex` +- `bibtex` (or `biber` if you later switch the bibliography backend) +- `latexmk` (recommended) +- packages used by `main.tex`: `geometry`, `times`, `graphicx`, `booktabs`, `hyperref`, `xcolor`, `natbib`, `setspace`, `lineno`, `url` + +### Quick build + +```bash +cd paper +make +``` + +Output: `paper/main.pdf`. + +### Without latexmk + +```bash +cd paper +make pdflatex-only +``` + +### Clean aux files + +```bash +make clean # keep PDF +make distclean # remove PDF too +``` + +### Overleaf + +Upload `main.tex`, `references.bib`, and `figures/` to a new Overleaf project. Set the compiler to pdfLaTeX. For final OUP formatting, replace the document class with the OUP General Template as linked above. + +## Word / page budget tips + +- Keep the body short: Motivation → Implementation → Results/use → brief Discussion. +- Prefer **one** main figure (pipeline placement or evidence summary). +- Put finding catalogs, long tables, and extra screenshots in Supplementary Information. +- Count words before submission; manuscripts that overrun the Application Note limit by a large margin are often returned without review. + +## Before submission checklist + +- [ ] Replace TBD emails, affiliation, funding, and benchmark table cells +- [ ] Drop real figure(s) into `figures/` and uncomment `\includegraphics` +- [ ] Freeze software version + DOI/archive (GitHub release / Zenodo) in Availability +- [ ] Align claims with `docs/product-thesis.md` and `docs/tool-landscape.md` (no “FastQC for FASTA”) +- [ ] Verify Bioconda / BioContainers / module links still resolve +- [ ] Optionally deposit a preprint (bioRxiv / arXiv q-bio) — OUP is preprint-friendly with update-on-accept obligations diff --git a/paper/figures/README.md b/paper/figures/README.md new file mode 100644 index 0000000..2cb5746 --- /dev/null +++ b/paper/figures/README.md @@ -0,0 +1,17 @@ +# Figures + +Place submission-ready artwork here. + +Suggested primary figure for the Application Note: + +| File (suggested) | Content | +| --- | --- | +| `preflight-overview.pdf` | Edge-to-edge schematic: FASTA → FastaGuard (PASS/WARN/FAIL + report bundle) → QUAST / BUSCO / BlobToolKit / MultiQC / submission validators | + +Optional supplements (not counted against the tight main-text budget if moved online-only): + +- HTML report screenshot +- Compare-mode cohort table screenshot +- Gate JSON excerpt highlighting `gate.can_continue` + +Export vector PDF or 350+ dpi PNG/TIFF for journal upload. diff --git a/paper/main.tex b/paper/main.tex new file mode 100644 index 0000000..e6ec2e8 --- /dev/null +++ b/paper/main.tex @@ -0,0 +1,173 @@ +% FastaGuard — Bioinformatics (OUP) Application Note draft +% Target: Application Note (~4 pages / ~2,000–2,600 words) +% Initial submissions may be format-free; use OUP General Template for revision. +% See README.md for build steps and venue notes. +% +% Compile: make (or: latexmk -pdf main.tex) +\pdfminorversion=5 +\documentclass[11pt,a4paper]{article} + +% Draft layout approximating journal constraints without requiring oup-authoring-template. +% Switch to OUP General Template (\documentclass[numsec,webpdf,modern,large]{...}) +% before camera-ready / revision formatting. +\usepackage[margin=2.5cm]{geometry} +\usepackage{times} +\usepackage{graphicx} +\usepackage{booktabs} +\usepackage{hyperref} +\usepackage{xcolor} +\usepackage{natbib} +\usepackage{url} +\usepackage{setspace} +\usepackage{lineno} + +\hypersetup{ + colorlinks=true, + linkcolor=black, + citecolor=blue!50!black, + urlcolor=blue!50!black +} + +% Double-spacing + line numbers help page-budget checks for first submission. +\doublespacing +\linenumbers + +\title{FastaGuard: FASTA preflight QC for modern bioinformatics pipelines} + +\author{ + Ehsan Estaji% + \thanks{Corresponding author. Email: \texttt{[institutional-email@example.org]}}% + \\ + \textit{[Affiliation to be completed]} +} + +\date{} + +\begin{document} +\maketitle + +\begin{abstract} +\noindent\textbf{Summary:} +FastaGuard is a fast, explainable FASTA preflight QC tool that validates assembly +FASTA files, detects structural and composition red flags, and emits a stable, +pipeline-ready report contract (HTML, JSON, TSV, MultiQC-compatible JSON) before +expensive downstream steps such as QUAST, BUSCO, BlobToolKit, CheckM, or annotation. + +\noindent\textbf{Availability and Implementation:} +Source, binaries, and documentation are freely available under the MIT license at +\url{https://github.com/ehsanestaji/FastaGuard}. +Install via Bioconda (\texttt{fastaguard}), BioContainers, or GitHub release binaries. +Implemented in Rust; supported on Linux and macOS. + +\noindent\textbf{Contact:} +\texttt{[institutional-email@example.org]} + +\noindent\textbf{Supplementary information:} +Supplementary figures, finding catalog excerpts, and reproducibility notes will +accompany the submitted manuscript (see \texttt{paper/} in the repository). +\end{abstract} + +\section*{Motivation} +FASTA files underpin genome assembly workflows, yet FASTA-level quality control +remains fragmented across ad hoc scripts, general sequence utilities, and heavy +downstream evaluators~\citep{gurevich2013quast,simao2015busco,challis2020blobtoolkit}. +Malformed structure, duplicate identifiers, invalid alphabet characters, high-N +content, and submission hazards often surface only after costly QC or official +repository validation has already started. + +FastaGuard fills the missing \emph{preflight} layer: a single, opinionated first +command that answers whether a FASTA is valid, sane, interpretable, and safe to +pass downstream, and that returns a deterministic PASS/WARN/FAIL contract for +humans, workflow engines, and tool agents~\citep{ewels2016multiqc}. +It is not a replacement for interpretive assembly QC; it is the earlier gate +that makes those tools safer and cheaper to run. + +\section*{Implementation and features} +FastaGuard is a streaming-first Rust CLI focused on assembly FASTA preflight. +Core capabilities include: + +\begin{itemize} + \item Structural validity checks (headers, empty records, parse failures) with + explainable FAIL verdicts. + \item Identifier and alphabet hygiene (duplicate IDs/sequences, invalid IUPAC). + \item Assembly stats and composition signals (N50/L50 family metrics; GC/AT/N + and ambiguity rates; gap runs; tiny-contig advisories; outlier hints). + \item Pipeline gates (\texttt{--gate pipeline}) exposing + \texttt{gate.can\_continue}, blocking vs advisory findings, and input + checksum provenance for stop/go routing in Nextflow, Snakemake, nf-core, + and related engines. + \item Submission-readiness preflight (\texttt{--gate submission}) for FASTA-level + NCBI genome policy signals without replacing official validators. + \item Cohort triage via \texttt{fastaguard compare} across many assemblies. + \item Reference compatibility mode for FASTA vs index/dictionary/header + declaration agreement (v1 reference contract). + \item Stable multi-format outputs: HTML report, versioned JSON schema, TSV, + and MultiQC-compatible JSON. +\end{itemize} + +\noindent +Install and smoke-test paths are intentionally low-friction (Bioconda, +BioContainers, and release binaries), so Application Note readers can reproduce +basic runs without heavy local builds. + +% Placeholder figure — replace with real pipeline schematic or benchmark plot. +\begin{figure}[t] + \centering + \fbox{\parbox{0.92\linewidth}{\centering\vspace{2.2cm} + \textbf{[Figure~1 placeholder]}\\[0.4em] + FastaGuard as the preflight layer before QUAST / BUSCO / BlobToolKit / MultiQC.\\[0.3em] + \textit{Replace with \texttt{figures/preflight-overview.pdf} or PNG.} + \vspace{2.2cm}}} + \caption{Conceptual placement of FastaGuard relative to common assembly QC tools. + FastaGuard answers FASTA validity and triage questions; interpretive QC remains + downstream.} + \label{fig:overview} +\end{figure} + +\section*{Results and use} +On representative public and synthetic assemblies, FastaGuard completes preflight +in seconds-scale wall time with modest memory, returning machine-readable +verdicts suitable for workflow branching +(see Table~\ref{tab:benchmark-placeholder} and repository evidence under +\texttt{docs/evidence/}). +Typical outcomes are: PASS with empty finding sets on clean references; +non-blocking WARN advisories (for example gap-pattern hints) that route follow-up +QC; and FAIL when structure, alphabet, or gate-blocking conditions are met. + +% Placeholder table — replace with measured public-evidence numbers before submission. +\begin{table}[t] + \centering + \caption{Illustrative preflight outcomes (replace with frozen public-evidence + numbers before submission).} + \label{tab:benchmark-placeholder} + \begin{tabular}{@{}lllrr@{}} + \toprule + Input role & Verdict & Gate continue & Time & Peak RSS \\ + \midrule + Clean public assembly & PASS & yes & TBD s & TBD MB \\ + Public assembly + advisories & WARN & yes & TBD s & TBD MB \\ + Synthetic contract FAIL case & FAIL & no & TBD s & TBD MB \\ + \bottomrule + \end{tabular} +\end{table} + +FastaGuard is already packaged for community workflows (Bioconda, BioContainers, +merged nf-core module and Snakemake wrapper updates). The citable artifact is +the versioned software contract plus this Application Note. + +\section*{Discussion} +Positioning FastaGuard as preflight—not as FastQC-for-FASTA or as a QUAST/BUSCO +competitor—keeps claims falsifiable and useful: catch FASTA-level problems early, +emit a stable schema, and let interpretive tools do interpretive work. +Remaining work for the published note includes locking public-evidence tables, +one overview figure, and a short finding-catalog supplement. + +\section*{Acknowledgements} +% Add acknowledgements, funding, and conflict disclosures as required by the venue. +The author thanks early adopters and workflow maintainers who exercised packaging +and module integrations. Funding: [to be completed]. + +\bibliographystyle{plainnat} +\bibliography{references} + +\end{document} diff --git a/paper/references.bib b/paper/references.bib new file mode 100644 index 0000000..72dce9f --- /dev/null +++ b/paper/references.bib @@ -0,0 +1,80 @@ +% Core citations for FastaGuard Application Note framing. +% Expand / verify DOIs and pages before submission. + +@article{gurevich2013quast, + author = {Gurevich, Alexey and Saveliev, Vladislav and Vyahhi, Nikolay and Tesler, Glenn}, + title = {{QUAST}: quality assessment tool for genome assemblies}, + journal = {Bioinformatics}, + year = {2013}, + volume = {29}, + number = {8}, + pages = {1072--1075}, + doi = {10.1093/bioinformatics/btt086} +} + +@article{simao2015busco, + author = {Sim{\~a}o, Felipe A. and Waterhouse, Robert M. and Ioannidis, Panagiotis and Kriventseva, Evgenia V. and Zdobnov, Evgeny M.}, + title = {{BUSCO}: assessing genome assembly and annotation completeness with single-copy orthologs}, + journal = {Bioinformatics}, + year = {2015}, + volume = {31}, + number = {19}, + pages = {3210--3212}, + doi = {10.1093/bioinformatics/btv351} +} + +@article{challis2020blobtoolkit, + author = {Challis, Richard and Richards, Edward and Rajan, Jeena and Cochrane, Guy and Blaxter, Mark}, + title = {{BlobToolKit} -- interactive quality assessment of genome assemblies}, + journal = {G3 Genes|Genomes|Genetics}, + year = {2020}, + volume = {10}, + number = {4}, + pages = {1361--1374}, + doi = {10.1534/g3.119.400908} +} + +@article{ewels2016multiqc, + author = {Ewels, Philip and Magnusson, M{\aa}ns and Lundin, Sverker and K{\"a}ller, Max}, + title = {{MultiQC}: summarize analysis results for multiple tools and samples in a single report}, + journal = {Bioinformatics}, + year = {2016}, + volume = {32}, + number = {19}, + pages = {3047--3048}, + doi = {10.1093/bioinformatics/btw354} +} + +@article{andrews2010fastqc, + author = {Andrews, Simon}, + title = {{FastQC}: a quality control tool for high throughput sequence data}, + year = {2010}, + note = {Available at \url{https://www.bioinformatics.babraham.ac.uk/projects/fastqc/}} +} + +@article{shen2016seqkit, + author = {Shen, Wei and Le, Shuai and Li, Yan and Hu, Fuquan}, + title = {{SeqKit}: a cross-platform and ultrafast toolkit for {FASTA}/{Q} file manipulation}, + journal = {PLOS ONE}, + year = {2016}, + volume = {11}, + number = {10}, + pages = {e0163962}, + doi = {10.1371/journal.pone.0163962} +} + +@misc{fastaguard2026, + author = {Estaji, Ehsan}, + title = {{FastaGuard}}, + year = {2026}, + howpublished = {\url{https://github.com/ehsanestaji/FastaGuard}}, + note = {Version 1.0.0; MIT License} +} + +@misc{ncbi_genome_submit, + author = {{National Center for Biotechnology Information}}, + title = {{NCBI} Genome Submission Guide}, + year = {2026}, + howpublished = {\url{https://www.ncbi.nlm.nih.gov/genbank/genomesubmit/}}, + note = {Accessed for FastaGuard ncbi\_genome policy snapshot} +} diff --git a/paper/tables/README.md b/paper/tables/README.md new file mode 100644 index 0000000..72ffcf9 --- /dev/null +++ b/paper/tables/README.md @@ -0,0 +1,14 @@ +# Tables + +Optional working tables / CSV exports that feed the manuscript. + +Suggested frozen evidence table for `main.tex` Table 1: + +- input role / accession +- FastaGuard version + commit +- verdict / gate status +- wall time +- peak RSS +- platform + +Source evidence packs live under `docs/evidence/` in the repository root; copy summarized numbers here rather than inventing new claims. From 160d4d1c97362a6ed537a3d3c7a25bcc0beb687e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 24 Oct 2026 14:28:26 +0000 Subject: [PATCH 2/9] Expand Bioinformatics Application Note draft and handoff docs Complete a full Application Note first draft (claims, gates, packaging, TikZ overview figure, v0.6 evidence table) and add paper/HANDOFF.md plus README updates so cold-start agents can resume the paper track. Signed-off-by: Cursor Agent Co-authored-by: ehsan.estaji --- paper/HANDOFF.md | 74 ++++++++++ paper/README.md | 81 ++++------- paper/main.tex | 358 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 366 insertions(+), 147 deletions(-) create mode 100644 paper/HANDOFF.md diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md new file mode 100644 index 0000000..96f65f2 --- /dev/null +++ b/paper/HANDOFF.md @@ -0,0 +1,74 @@ +# Paper track handoff (cold-start) + +For any agent or human resuming FastaGuard **paper preparation** after a pause. +This track is **paper-only**. Do not change product CLI/schema/packaging unless +the user explicitly expands scope. Product/dev work may be on a separate track +(e.g. Aragon / other agents) — do not conflict with it. + +## Locked decisions + +| Item | Value | +| --- | --- | +| Venue | ***Bioinformatics* (OUP) — Application Note** (**user-confirmed**) | +| Working title | `FastaGuard: FASTA preflight QC for modern bioinformatics pipelines` | +| Framing | FASTA **preflight** contract before QUAST/BUSCO/BlobToolKit/etc. | +| Forbidden phrase | Do **not** use “FastQC for FASTA” | +| Repo branch / PR | `cursor/paper-prep-venues-198e` / [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) | +| LaTeX root | `paper/main.tex` | + +## What is done + +- Full Application Note **first draft** in `main.tex` (Motivation, Implementation, Results, Discussion). +- Structured abstract (Summary / Availability and Implementation / Contact / Supplementary). +- TikZ main figure (preflight placement story). +- Evidence table filled from committed v0.6 public-evidence pack (`docs/evidence/`). +- BibTeX `references.bib` with core ecosystem citations. +- `Makefile` + build README; PDF builds with TeX Live (`pdflatex` + `bibtex` via `latexmk`). +- Project store venue brief + project context (paths below). + +## Draft vs final + +| Piece | Status | +| --- | --- | +| Scientific framing / claims | Draft-complete; aligned with `docs/product-thesis.md` | +| Author email / affiliation | **TBD placeholders** | +| Funding / COI details | **TBD** (COI currently “none declared”) | +| Figure | **Usable TikZ**; optional polish / export to PDF art later | +| Table numbers | From **v0.6** evidence (contextual timings); re-freeze if citing newer release | +| Word count | Aim ~2,000–2,600; recount after edits (`paper/HANDOFF.md` workflow) | +| OUP camera-ready template | **Not yet** — format-free OK for first submission | +| Preprint / Zenodo DOI | **Not yet** | +| Submission to ScholarOne | **Not yet** | + +## How to build + +```bash +cd paper +make # latexmk -pdf main.tex → main.pdf +make clean # aux only +``` + +Requires: `pdflatex`, `bibtex`, `latexmk`, and packages used in `main.tex` +(including `tikz` from `texlive-pictures`). PDF is gitignored. + +## Store docs (durable Project Context) + +- Venue + framing: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/publication-venues.md` +- Project context / resume: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` + +(Also via symlink `/cursor/stores/self/docs/...` when that alias is mounted.) + +## Next concrete manuscript steps + +1. Replace `[institutional-email@example.org]` and affiliation; complete funding. +2. Recount words; trim or expand to stay near Application Note budget (~2k–2.6k words / ~4 typeset pages). Enable `\doublespacing` before ScholarOne upload if desired. +3. Optionally re-run evidence for the exact software version named in Availability (v1.0.0 packaging vs newer source tag) and update Table 1. +4. Deposit preprint (bioRxiv) and/or archive DOI (Zenodo) if desired before or with submission. +5. Submit format-free PDF + SI; on revision, switch to OUP General Template. +6. Confirm APC / Read & Publish / ISCB discount before acceptance. + +## Key in-repo sources for claims + +- `README.md`, `docs/product-thesis.md`, `docs/tool-landscape.md`, `docs/output-contract.md` +- `docs/evidence/fastaguard-v0.6-public-evidence.md` + `docs/evidence/results/v0.6/` +- `CITATION.cff` diff --git a/paper/README.md b/paper/README.md index 1d9db85..8ea3e67 100644 --- a/paper/README.md +++ b/paper/README.md @@ -1,83 +1,60 @@ # FastaGuard paper (LaTeX) -Draft manuscript targeting **Bioinformatics (Oxford Academic) — Application Note**. +**Venue (user-confirmed):** [*Bioinformatics*](https://academic.oup.com/bioinformatics) (Oxford Academic) — **Application Note**. -This folder is paper preparation only. It does not change the FastaGuard product. +**PR / branch:** [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · `cursor/paper-prep-venues-198e` -## Target venue (primary) +This folder is **paper preparation only**. It does not change the FastaGuard product. +Cold-start resume details: see [`HANDOFF.md`](HANDOFF.md). -| Item | Choice | +## Article constraints + +| Item | Target | | --- | --- | -| Journal | [*Bioinformatics*](https://academic.oup.com/bioinformatics) (OUP) | -| Article type | Application Note | -| Length budget | Up to ~4 pages (~2,600 words, or ~2,000 words + one figure) | -| Abstract shape | Summary / Availability and Implementation / Contact / Supplementary information | -| Software rules | Freely available to non-commercial users; state availability + implementation; keep available ≥2 years after publication | +| Type | Application Note | +| Length | Up to ~4 pages (~2,600 words, or ~2,000 words + one figure) | +| Title | Must include the application name (`FastaGuard: …`) | +| Abstract | Summary / Availability and Implementation / Contact / Supplementary information | +| Software | Freely available; state availability + implementation; keep available ≥2 years | Format-free initial submission is allowed. For revision / camera-ready, switch to the [OUP General Template](https://www.overleaf.com/latex/templates/oup-general-template/fqkhysbcbpwv) with Bioinformatics options (`numsec,webpdf,modern,large`) and author-date bibliography style as documented by OUP. -Venue ranking and framing notes for authors live in the Project store doc `docs/publication-venues.md` (not in this repo path). - -## Layout of this folder +## Layout ```text paper/ - main.tex Application Note skeleton (claims + section outline) + main.tex Full Application Note draft references.bib BibTeX database Makefile latexmk / pdflatex build README.md this file - figures/ figure placeholders (add PDF/PNG before submission) - tables/ optional table source / CSV exports + HANDOFF.md cold-start status + next steps + figures/ notes for optional external artwork + tables/ optional CSV / working tables ``` ## Build the PDF -Requires a standard TeX Live (or MacTeX / MiKTeX) install with: - -- `pdflatex` -- `bibtex` (or `biber` if you later switch the bibliography backend) -- `latexmk` (recommended) -- packages used by `main.tex`: `geometry`, `times`, `graphicx`, `booktabs`, `hyperref`, `xcolor`, `natbib`, `setspace`, `lineno`, `url` - -### Quick build - -```bash -cd paper -make -``` - -Output: `paper/main.pdf`. - -### Without latexmk +Requires TeX Live (or MacTeX / MiKTeX) with `pdflatex`, `bibtex`, `latexmk`, plus +`geometry`, `times`, `graphicx`, `booktabs`, `hyperref`, `xcolor`, `natbib`, +`setspace`, `lineno`, `url`, and **TikZ** (`texlive-pictures`). ```bash cd paper +make # → main.pdf make pdflatex-only -``` - -### Clean aux files - -```bash make clean # keep PDF make distclean # remove PDF too ``` -### Overleaf - -Upload `main.tex`, `references.bib`, and `figures/` to a new Overleaf project. Set the compiler to pdfLaTeX. For final OUP formatting, replace the document class with the OUP General Template as linked above. +Overleaf: upload `main.tex`, `references.bib`, and compile with pdfLaTeX. -## Word / page budget tips +## Draft status (short) -- Keep the body short: Motivation → Implementation → Results/use → brief Discussion. -- Prefer **one** main figure (pipeline placement or evidence summary). -- Put finding catalogs, long tables, and extra screenshots in Supplementary Information. -- Count words before submission; manuscripts that overrun the Application Note limit by a large margin are often returned without review. +- **Done:** full draft body, TikZ overview figure, evidence table from v0.6 public pack, BibTeX, build path. +- **Still TBD before submission:** author email/affiliation, funding, optional version-frozen re-evidence, preprint/DOI, OUP template on revision. +- **Framing rule:** never pitch as “FastQC for FASTA.” -## Before submission checklist +Durable venue + project context for multi-agent handoff live in the Project store: -- [ ] Replace TBD emails, affiliation, funding, and benchmark table cells -- [ ] Drop real figure(s) into `figures/` and uncomment `\includegraphics` -- [ ] Freeze software version + DOI/archive (GitHub release / Zenodo) in Availability -- [ ] Align claims with `docs/product-thesis.md` and `docs/tool-landscape.md` (no “FastQC for FASTA”) -- [ ] Verify Bioconda / BioContainers / module links still resolve -- [ ] Optionally deposit a preprint (bioRxiv / arXiv q-bio) — OUP is preprint-friendly with update-on-accept obligations +- `docs/publication-venues.md` +- `docs/project-context.md` diff --git a/paper/main.tex b/paper/main.tex index e6ec2e8..1533358 100644 --- a/paper/main.tex +++ b/paper/main.tex @@ -1,15 +1,16 @@ -% FastaGuard — Bioinformatics (OUP) Application Note draft -% Target: Application Note (~4 pages / ~2,000–2,600 words) -% Initial submissions may be format-free; use OUP General Template for revision. -% See README.md for build steps and venue notes. +% FastaGuard — Bioinformatics (OUP) Application Note +% USER-CONFIRMED venue: Application Note (~4 pages / ~2,000–2,600 words) +% Format-free first submission OK; use OUP General Template for revision. +% Build: make (see README.md and HANDOFF.md) % -% Compile: make (or: latexmk -pdf main.tex) +% Status: full first draft for review — author email/affiliation/funding still TBD; +% TikZ figure is submission-usable; table numbers from committed v0.6 evidence. \pdfminorversion=5 \documentclass[11pt,a4paper]{article} % Draft layout approximating journal constraints without requiring oup-authoring-template. -% Switch to OUP General Template (\documentclass[numsec,webpdf,modern,large]{...}) -% before camera-ready / revision formatting. +% For camera-ready / revision, switch to the OUP General Template with Bioinformatics +% options: \documentclass[numsec,webpdf,modern,large]{...} and author-date bib style. \usepackage[margin=2.5cm]{geometry} \usepackage{times} \usepackage{graphicx} @@ -20,6 +21,8 @@ \usepackage{url} \usepackage{setspace} \usepackage{lineno} +\usepackage{tikz} +\usetikzlibrary{arrows.meta,positioning,fit,calc} \hypersetup{ colorlinks=true, @@ -28,8 +31,9 @@ urlcolor=blue!50!black } -% Double-spacing + line numbers help page-budget checks for first submission. -\doublespacing +% Single spacing approximates final page budget while drafting. +% For ScholarOne first submission, enable \doublespacing and keep \linenumbers. +% \doublespacing \linenumbers \title{FastaGuard: FASTA preflight QC for modern bioinformatics pipelines} @@ -49,123 +53,287 @@ \begin{abstract} \noindent\textbf{Summary:} FastaGuard is a fast, explainable FASTA preflight QC tool that validates assembly -FASTA files, detects structural and composition red flags, and emits a stable, -pipeline-ready report contract (HTML, JSON, TSV, MultiQC-compatible JSON) before -expensive downstream steps such as QUAST, BUSCO, BlobToolKit, CheckM, or annotation. +FASTA files, detects structural and composition red flags, and emits a stable +multi-format report contract before expensive downstream analysis. \noindent\textbf{Availability and Implementation:} -Source, binaries, and documentation are freely available under the MIT license at +Freely available under the MIT license at \url{https://github.com/ehsanestaji/FastaGuard}. -Install via Bioconda (\texttt{fastaguard}), BioContainers, or GitHub release binaries. -Implemented in Rust; supported on Linux and macOS. +Install via Bioconda (\texttt{mamba install -c bioconda fastaguard}), BioContainers, +or GitHub release binaries. Implemented in Rust; supported on Linux and macOS. +Published packaging targets include Bioconda builds for \texttt{linux-64}, +\texttt{linux-aarch64}, \texttt{osx-64}, and \texttt{osx-arm64}, plus a pinned +BioContainers image. \noindent\textbf{Contact:} \texttt{[institutional-email@example.org]} \noindent\textbf{Supplementary information:} -Supplementary figures, finding catalog excerpts, and reproducibility notes will -accompany the submitted manuscript (see \texttt{paper/} in the repository). +Supplementary notes, finding-catalog excerpts, and reproducibility pointers are +maintained with the software repository under \texttt{docs/} and \texttt{paper/}. \end{abstract} \section*{Motivation} -FASTA files underpin genome assembly workflows, yet FASTA-level quality control -remains fragmented across ad hoc scripts, general sequence utilities, and heavy -downstream evaluators~\citep{gurevich2013quast,simao2015busco,challis2020blobtoolkit}. -Malformed structure, duplicate identifiers, invalid alphabet characters, high-N -content, and submission hazards often surface only after costly QC or official -repository validation has already started. - -FastaGuard fills the missing \emph{preflight} layer: a single, opinionated first -command that answers whether a FASTA is valid, sane, interpretable, and safe to -pass downstream, and that returns a deterministic PASS/WARN/FAIL contract for -humans, workflow engines, and tool agents~\citep{ewels2016multiqc}. -It is not a replacement for interpretive assembly QC; it is the earlier gate -that makes those tools safer and cheaper to run. - -\section*{Implementation and features} -FastaGuard is a streaming-first Rust CLI focused on assembly FASTA preflight. -Core capabilities include: - -\begin{itemize} - \item Structural validity checks (headers, empty records, parse failures) with - explainable FAIL verdicts. - \item Identifier and alphabet hygiene (duplicate IDs/sequences, invalid IUPAC). - \item Assembly stats and composition signals (N50/L50 family metrics; GC/AT/N - and ambiguity rates; gap runs; tiny-contig advisories; outlier hints). - \item Pipeline gates (\texttt{--gate pipeline}) exposing - \texttt{gate.can\_continue}, blocking vs advisory findings, and input - checksum provenance for stop/go routing in Nextflow, Snakemake, nf-core, - and related engines. - \item Submission-readiness preflight (\texttt{--gate submission}) for FASTA-level - NCBI genome policy signals without replacing official validators. - \item Cohort triage via \texttt{fastaguard compare} across many assemblies. - \item Reference compatibility mode for FASTA vs index/dictionary/header - declaration agreement (v1 reference contract). - \item Stable multi-format outputs: HTML report, versioned JSON schema, TSV, - and MultiQC-compatible JSON. -\end{itemize} +Genome assemblies and reference FASTA files are foundational inputs for +bioinformatics pipelines, yet FASTA-level quality control remains fragmented. +Teams commonly combine ad hoc scripts, general sequence utilities such as +SeqKit~\citep{shen2016seqkit}, assembly evaluators such as +QUAST~\citep{gurevich2013quast}, biological completeness tools such as +BUSCO~\citep{simao2015busco}, contamination-oriented exploration with +BlobToolKit~\citep{challis2020blobtoolkit}, and late-stage report aggregation with +MultiQC~\citep{ewels2016multiqc}. +Raw-read QC tools remain essential before assembly, but they do not answer +whether a finished assembly FASTA is structurally sound for the next stage. +Each of the tools above is valuable for its intended question. +None is the simple default first command for a narrower but frequent need: +whether a FASTA file is structurally valid, alphabet-sane, interpretable, and +safe to pass into expensive downstream steps. + +The cost of skipping that earlier layer is practical. +Malformed headers, empty records, duplicate identifiers, invalid IUPAC +characters, extreme ambiguity, and high-N scaffolds often surface only after +minutes to hours of assembly QC, completeness assessment, taxonomy workflows, +annotation, or official repository validation have already started. +Pipeline authors then reinvent the same checks repeatedly, with incompatible +report shapes and unclear stop/go semantics for workflow engines. +SeqKit and related utilities can compute counts and lengths quickly, but they +are not opinionated QC gates with a versioned schema, MultiQC-compatible +artifacts, and explicit blocking-versus-advisory policy for Nextflow or +Snakemake. + +FastaGuard addresses that gap as a dedicated \emph{preflight} layer +(Fig.~\ref{fig:overview}). +It is intentionally not a competitor to interpretive assembly QC: +it does not claim biological completeness, contamination taxonomy, or repository +acceptance, and it does not replace official NCBI, ENA, or DDBJ validators. +Instead it answers whether the FASTA itself is ready for those tools, and it +returns a deterministic, versioned PASS/WARN/FAIL contract that humans, +Nextflow/Snakemake/nf-core workflows, and future tool-using agents can consume +without scraping logs or HTML. +The product thesis is therefore a contract as much as a report: +stable field names, documented finding identifiers, provenance checksums, and +gate continuation booleans that pipelines can depend on across releases. + +\section*{Implementation} +FastaGuard is a streaming-first command-line tool implemented in Rust and +distributed as a single binary. +The primary assembly workflow is: + +\begin{verbatim} +fastaguard sample.fa --profile assembly --gate pipeline \ + --outdir reports --prefix sample-01 +\end{verbatim} \noindent -Install and smoke-test paths are intentionally low-friction (Bioconda, -BioContainers, and release binaries), so Application Note readers can reproduce -basic runs without heavy local builds. +which writes a deterministic four-report bundle +(\texttt{.html}, \texttt{.json}, \texttt{.tsv}, and MultiQC-compatible +\texttt{\_mqc.json}) under a chosen prefix. +JSON is the source of truth for machine routing. +A committed JSON Schema and finding catalog document field names, finding +identifiers, severity semantics, and suggested actions +(\texttt{fastaguard --schema}, \texttt{--finding-catalog}, +\texttt{--explain-finding}). + +\paragraph{Validity and composition checks.} +Under the assembly profile, FastaGuard reports structural failures (malformed +headers, empty records, sequence before the first header, empty input), +duplicate identifiers and duplicate sequences, invalid nucleotide/IUPAC +symbols, core length statistics including N50/N90 and L50/L90, and composition +signals (GC/AT/N and ambiguity rates, gap runs, tiny-contig advisories, and +composition or length outlier hints). +Findings are explainable: each carries an identifier, evidence bounds, and +routing language that distinguishes blockers from advisories. + +\paragraph{Pipeline gates and process exit contract.} +Optional gates make stop/go policy explicit. +The recommended workflow preset \texttt{--gate pipeline} marks duplicate IDs, +invalid characters, invalid FASTA structure, and high-N content as blocking by +default, while GC and length outliers remain advisory routing signals unless +elevated with \texttt{--fail-on}. +JSON exposes \texttt{gate.status}, \texttt{gate.blocking\_findings}, +\texttt{gate.advisory\_findings}, \texttt{gate.can\_continue}, and +\texttt{provenance.input\_sha256}. +From v0.6 onward, process exit codes separate execution from QC status: +exit~0 means report generation completed for PASS, WARN, and FAIL results; +exit~2 covers argument errors; exit~3 covers configuration, I/O, or runtime +failures. +Workflows should therefore branch on \texttt{gate.can\_continue} (or related +gate fields), not on process status alone. +\texttt{machine\_summary.safe\_for\_downstream} is intentionally stricter (true +only for overall PASS) and is not a synonym for gate continuation. + +\paragraph{Submission-readiness and compare mode.} +\texttt{--gate submission} with \texttt{--submission-target generic|ncbi} +performs FASTA-level submission preflight before official validators. +For NCBI genome targeting, FastaGuard applies a documented +\texttt{ncbi\_genome} policy snapshot (first-token SeqID syntax and length +limits, minimum record length, terminal Ns) derived from the NCBI Genome +Submission Guide~\citep{ncbi_genome_submit}; it does not replace NCBI FCS, +annotation validation, or repository acceptance decisions. +\texttt{fastaguard compare} triages many FASTA files into cohort HTML/JSON/TSV +outputs for starter cohort routing before interpretive QC. +A separate \texttt{reference} mode checks whether explicitly supplied companion +declarations (FASTA index, sequence dictionary, alignment/variant headers, +GFF3 sequence declarations) agree with a canonical FASTA under +\texttt{strict}, \texttt{coordinate}, or \texttt{advisory} policies. + +\paragraph{Readiness categories.} +Beyond a single verdict, reports expose readiness facets spanning file, +structure, alphabet, index, assembly, submission, and machine concerns. +That decomposition helps operators see \emph{why} a file is not yet ready +(for example, identifier hazards versus composition advisories) without reading +an unstructured log. +HTML reports remain human-oriented, while JSON/TSV remain stable for engines +and for MultiQC custom content. + +\paragraph{Packaging and workflow adoption.} +FastaGuard is published on Bioconda and BioContainers, ships GitHub release +binaries for major Linux and macOS targets, and has merged nf-core module and +Snakemake wrapper integrations for assembly preflight. +A typical install path is +\texttt{mamba install -c conda-forge -c bioconda fastaguard=1.0.0}, with a +pinned BioContainers image for containerized workflow engines. +These distribution paths are part of the Application Note claim: the tool is +meant to be installed and gated in real pipelines with low friction, not only +built from source by specialists. +Availability commitments follow the journal software policy: source and +published packages remain openly reachable from the project repository under +the MIT license~\citep{fastaguard2026}. -% Placeholder figure — replace with real pipeline schematic or benchmark plot. \begin{figure}[t] \centering - \fbox{\parbox{0.92\linewidth}{\centering\vspace{2.2cm} - \textbf{[Figure~1 placeholder]}\\[0.4em] - FastaGuard as the preflight layer before QUAST / BUSCO / BlobToolKit / MultiQC.\\[0.3em] - \textit{Replace with \texttt{figures/preflight-overview.pdf} or PNG.} - \vspace{2.2cm}}} - \caption{Conceptual placement of FastaGuard relative to common assembly QC tools. - FastaGuard answers FASTA validity and triage questions; interpretive QC remains - downstream.} + \begin{tikzpicture}[ + font=\scriptsize\sffamily, + >=Latex, + box/.style={draw, rounded corners=2pt, align=center, inner sep=5pt, + minimum height=0.85cm, minimum width=2.15cm}, + io/.style={box, fill=gray!12}, + fg/.style={box, fill=blue!8, thick, minimum width=2.6cm}, + down/.style={box, fill=orange!10, minimum width=1.55cm}, + arr/.style={->, thick}, + lab/.style={font=\scriptsize\sffamily\itshape, text=black!70} + ] + \node[io] (fa) {Assembly\\FASTA}; + \node[fg, right=0.55cm of fa] (fg) {FastaGuard\\preflight}; + \node[io, right=0.55cm of fg] (rep) {HTML / JSON\\TSV / MultiQC}; + \node[down, below left=0.85cm and -0.15cm of rep] (q) {QUAST}; + \node[down, below=0.85cm of rep] (b) {BUSCO}; + \node[down, below right=0.85cm and -0.15cm of rep] (bl) {BlobToolKit\\/ CheckM}; + \node[io, right=0.7cm of rep] (sub) {Official\\validators}; + \draw[arr] (fa) -- (fg); + \draw[arr] (fg) -- (rep); + \draw[arr] (rep.south west) -- (q.north); + \draw[arr] (rep.south) -- (b.north); + \draw[arr] (rep.south east) -- (bl.north); + \draw[arr] (rep) -- (sub); + \node[lab, above=0.05cm of fg] {PASS / WARN / FAIL; \texttt{gate.can\_continue}}; + \node[lab, below=1.55cm of fg] + {Fix FASTA-level blockers first; interpretive QC remains downstream}; + \end{tikzpicture} + \caption{FastaGuard as the FASTA preflight layer before interpretive assembly + QC and official submission validation. + Machine-readable reports support workflow stop/go decisions without replacing + QUAST, BUSCO, BlobToolKit, CheckM, MultiQC, or repository validators.} \label{fig:overview} \end{figure} -\section*{Results and use} -On representative public and synthetic assemblies, FastaGuard completes preflight -in seconds-scale wall time with modest memory, returning machine-readable -verdicts suitable for workflow branching -(see Table~\ref{tab:benchmark-placeholder} and repository evidence under -\texttt{docs/evidence/}). -Typical outcomes are: PASS with empty finding sets on clean references; -non-blocking WARN advisories (for example gap-pattern hints) that route follow-up -QC; and FAIL when structure, alphabet, or gate-blocking conditions are met. - -% Placeholder table — replace with measured public-evidence numbers before submission. +\section*{Results} +Committed public-evidence summaries for FastaGuard v0.6.0 illustrate the intended +behaviour on synthetic contract fixtures and two exact NCBI reference assemblies +(Table~\ref{tab:evidence}). +Portable JSON and TSV summaries in the repository record input byte sizes, +SHA-256 digests, structural metrics, finding identifiers, and scoped provenance +separately for the observed executable and the verified source tree. +On a recorded macOS arm64 run, a clean \emph{Escherichia coli} K-12 MG1655 +assembly (\texttt{GCF\_000005845.2}; 4.64~Mbp) completed pipeline-gated +preflight in 0.06~s with PASS and no findings. +A \emph{Neurospora crassa} OR74A assembly (\texttt{GCF\_000182925.2}; 41.1~Mbp) +completed in 0.34~s with WARN for gap-pattern advisories and +\texttt{gate.can\_continue} still true under the pipeline gate, routing to deeper +assembly or composition follow-up rather than hard failure. +An intentionally broken local fixture returned FAIL with blocking findings +(duplicate IDs, invalid characters, high-N content) while still exiting~0 after +writing a complete FAIL report---matching the v0.6 workflow contract that +separates QC status from process success. +Earlier local value-benchmark runs on synthetic 10--100~Mbp FASTA inputs +likewise completed in under a second with modest RSS~\citep{fastaguard2026}, +supporting the claim that preflight overhead is small relative to typical +downstream QC. + +These examples are contract demonstrations, not a representative biological +benchmark across taxa or assemblers. +They show that clean references can pass quickly, that advisory gap patterns can +WARN without blocking continuation, and that FASTA-level blockers remain machine +visible in FAIL reports that workflow engines can parse. +Compare mode and submission gates extend the same contract to cohort triage and +FASTA-level submission hazards; reference mode adds companion-declaration +compatibility checks when indexes, dictionaries, or headers must agree with a +canonical FASTA before mapping or variant workflows proceed. + \begin{table}[t] \centering - \caption{Illustrative preflight outcomes (replace with frozen public-evidence - numbers before submission).} - \label{tab:benchmark-placeholder} - \begin{tabular}{@{}lllrr@{}} + \caption{Illustrative FastaGuard v0.6.0 public-evidence outcomes (assembly + profile, pipeline gate). Elapsed times are single-machine contextual + measurements from the committed evidence pack, not cross-platform guarantees. + Full portable JSON/TSV summaries are in the software repository.} + \label{tab:evidence} + \begin{tabular}{@{}llllr@{}} \toprule - Input role & Verdict & Gate continue & Time & Peak RSS \\ + Input & Scale & Verdict & Gate & Time (s) \\ \midrule - Clean public assembly & PASS & yes & TBD s & TBD MB \\ - Public assembly + advisories & WARN & yes & TBD s & TBD MB \\ - Synthetic contract FAIL case & FAIL & no & TBD s & TBD MB \\ + Synthetic valid fixture & 604~bp / 4 seqs & PASS & PASS & 0.019 \\ + Problem fixture & 145~bp / 5 seqs & FAIL & FAIL & 0.019 \\ + \emph{E.\ coli} \texttt{GCF\_000005845.2} & 4.64~Mbp / 1 seq & PASS & PASS & 0.057 \\ + \emph{N.\ crassa} \texttt{GCF\_000182925.2} & 41.1~Mbp / 21 seqs & WARN & WARN & 0.344 \\ \bottomrule \end{tabular} \end{table} -FastaGuard is already packaged for community workflows (Bioconda, BioContainers, -merged nf-core module and Snakemake wrapper updates). The citable artifact is -the versioned software contract plus this Application Note. +A minimal workflow pattern is therefore: run FastaGuard with +\texttt{--gate pipeline} and an output bundle; inspect +\texttt{gate.can\_continue} in JSON (for example with \texttt{jq}); only then +launch QUAST, BUSCO, BlobToolKit, CheckM, annotation, or official validators. +If the gate fails, fix the FASTA-level blockers first. +If the gate continues with WARN advisories, keep the findings as routing hints +for which interpretive tool to prioritize next. +In practice, a PASS or non-blocking WARN means only that the selected FastaGuard +gate policy was satisfied and that the FASTA can be routed to appropriate +downstream tools. +It does not establish assembly correctness, biological completeness, +contamination status, taxonomy, annotation quality, or archive acceptance. +That boundary is deliberate: FastaGuard owns FASTA-level preflight and triage; +interpretive tools own interpretive questions. \section*{Discussion} -Positioning FastaGuard as preflight—not as FastQC-for-FASTA or as a QUAST/BUSCO -competitor—keeps claims falsifiable and useful: catch FASTA-level problems early, -emit a stable schema, and let interpretive tools do interpretive work. -Remaining work for the published note includes locking public-evidence tables, -one overview figure, and a short finding-catalog supplement. +FastaGuard's contribution is a pipeline-native FASTA preflight \emph{contract}: +streaming validation, explainable findings, stable schemas, explicit gates, and +packaging paths that workflow authors can adopt without reinventing per-pipeline +scripts. +Relative to SeqKit, FastaGuard trades general sequence manipulation for an +opinionated QC verdict and gate policy. +Relative to QUAST, BUSCO, BlobToolKit, and CheckM, it runs earlier and answers a +different question: whether the FASTA is safe to spend those tools on. +Relative to MultiQC, it is a producer of aggregatable content rather than an +aggregator. +Positioning the tool as preflight---rather than as a substitute for those +ecosystem components---keeps claims falsifiable and complementary. + +Limitations follow from that scope. +FastaGuard does not discover biological contamination, does not certify archive +acceptance, and does not replace official validators or FCS-style screens. +Public-evidence timings are contextual to one machine and release line; authors +should re-measure on their targets before citing absolute performance. +The present Application Note freezes the v1.x reference-contract story suitable +for citation; feature expansion beyond FASTA preflight belongs in software +releases and, if warranted later, a separate research article rather than an +overloaded note. \section*{Acknowledgements} -% Add acknowledgements, funding, and conflict disclosures as required by the venue. -The author thanks early adopters and workflow maintainers who exercised packaging -and module integrations. Funding: [to be completed]. +Funding: [to be completed]. +Conflict of interest: none declared. +The software repository acknowledgements and release notes credit packaging and +workflow integration milestones (Bioconda, BioContainers, nf-core modules, and +Snakemake wrappers). \bibliographystyle{plainnat} \bibliography{references} From 6b3b22b364f821ff0e8b1043d98ee2519175bce8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 24 Oct 2026 14:52:12 +0000 Subject: [PATCH 3/9] Align Application Note front-matter with VariantFlow conventions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse author/ORCID/Umeå affiliation, WIFORCE funding, COI, AI disclosure, CRediT, and acknowledgements from the VariantFlow Bioinformatics note. Keep FastaGuard-specific evidence, claims, and bibliography; expand handoff docs for cold-start agents. Signed-off-by: Cursor Agent Co-authored-by: ehsan.estaji --- paper/HANDOFF.md | 82 ++++++++++++++++++------------ paper/README.md | 5 +- paper/main.tex | 127 +++++++++++++++++++++++++++++++---------------- 3 files changed, 135 insertions(+), 79 deletions(-) diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md index 96f65f2..c806db3 100644 --- a/paper/HANDOFF.md +++ b/paper/HANDOFF.md @@ -3,7 +3,7 @@ For any agent or human resuming FastaGuard **paper preparation** after a pause. This track is **paper-only**. Do not change product CLI/schema/packaging unless the user explicitly expands scope. Product/dev work may be on a separate track -(e.g. Aragon / other agents) — do not conflict with it. +— do not conflict with it. ## Locked decisions @@ -15,60 +15,76 @@ the user explicitly expands scope. Product/dev work may be on a separate track | Forbidden phrase | Do **not** use “FastQC for FASTA” | | Repo branch / PR | `cursor/paper-prep-venues-198e` / [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) | | LaTeX root | `paper/main.tex` | +| Author / funding template source | [VariantFlow](https://github.com/ehsanestaji/VariantFlow) `paper/bioinformatics-application-note/` (**user-directed reuse**) | + +## What was copied from VariantFlow (front-matter only) + +Reuse these from VariantFlow’s Application Note — **not** its VCF science: + +- Author list: Ehsan Estaji + Jian-Feng Mao, ORCIDs, Umeå Plant Science Centre affiliation +- Correspondence emails: `ehsan.estaji@umu.se`, `jianfeng.mao@umu.se` +- CRediT **Author contributions** wording pattern +- **Funding:** WIFORCE / Knut and Alice Wallenberg Foundation +- **Conflict of interest:** no competing interests +- **AI usage disclosure** pattern (adapted to FastaGuard / Cursor agents) +- **Acknowledgements:** SNIC/HPC2N at Umeå University (+ FastaGuard packaging thanks) +- Sectioning: Introduction / Implementation / Results / Discussion + end-matter blocks +- LaTeX front-matter conventions (margin, ORCID hyperlinks, `apalike`, keywords line) + +## What must stay FastaGuard-specific + +- All scientific claims, comparison framing, gates/contract description +- Evidence table and timings from `docs/evidence/` (v0.6 public pack) — **never** VariantFlow VCF benchmarks/tables +- Availability URLs, Bioconda/BioContainers/nf-core/Snakemake packaging story +- TikZ preflight figure and FastaGuard `references.bib` (QUAST/BUSCO/etc.) ## What is done -- Full Application Note **first draft** in `main.tex` (Motivation, Implementation, Results, Discussion). -- Structured abstract (Summary / Availability and Implementation / Contact / Supplementary). -- TikZ main figure (preflight placement story). -- Evidence table filled from committed v0.6 public-evidence pack (`docs/evidence/`). -- BibTeX `references.bib` with core ecosystem citations. -- `Makefile` + build README; PDF builds with TeX Live (`pdflatex` + `bibtex` via `latexmk`). -- Project store venue brief + project context (paths below). +- Full Application Note draft with VariantFlow-aligned author/funding/disclosures +- Structured Bioinformatics Application Note abstract (Summary / Availability / Contact / SI) +- TikZ overview figure; v0.6 evidence table +- ~2.0k words (within ~2.0–2.6k Application Note target); PDF builds via `make` +- Store context + venue brief updated ## Draft vs final | Piece | Status | | --- | --- | -| Scientific framing / claims | Draft-complete; aligned with `docs/product-thesis.md` | -| Author email / affiliation | **TBD placeholders** | -| Funding / COI details | **TBD** (COI currently “none declared”) | -| Figure | **Usable TikZ**; optional polish / export to PDF art later | -| Table numbers | From **v0.6** evidence (contextual timings); re-freeze if citing newer release | -| Word count | Aim ~2,000–2,600; recount after edits (`paper/HANDOFF.md` workflow) | +| Author / ORCID / affiliation / emails | **Filled from VariantFlow** (same author block by user default) | +| Funding / COI / AI disclosure / CRediT | **Filled** (VariantFlow patterns) | +| Scientific body + evidence | **FastaGuard draft-complete** | +| Word count | ~2.0k — OK; trim if journal typesetting overruns | +| Figure | Usable TikZ | +| Table | v0.6 evidence (contextual timings) | +| Zenodo / preprint DOI | **Not yet** (VariantFlow had these; add for FastaGuard when archived) | | OUP camera-ready template | **Not yet** — format-free OK for first submission | -| Preprint / Zenodo DOI | **Not yet** | -| Submission to ScholarOne | **Not yet** | +| ScholarOne submit | **Not yet** | ## How to build ```bash cd paper -make # latexmk -pdf main.tex → main.pdf -make clean # aux only +make # → main.pdf +make clean ``` -Requires: `pdflatex`, `bibtex`, `latexmk`, and packages used in `main.tex` -(including `tikz` from `texlive-pictures`). PDF is gitignored. - -## Store docs (durable Project Context) +Requires TeX Live with `pdflatex`, `bibtex`, `latexmk`, TikZ (`texlive-pictures`), `microtype`. -- Venue + framing: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/publication-venues.md` -- Project context / resume: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` +## Store docs -(Also via symlink `/cursor/stores/self/docs/...` when that alias is mounted.) +- `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/publication-venues.md` +- `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` -## Next concrete manuscript steps +## Next concrete steps -1. Replace `[institutional-email@example.org]` and affiliation; complete funding. -2. Recount words; trim or expand to stay near Application Note budget (~2k–2.6k words / ~4 typeset pages). Enable `\doublespacing` before ScholarOne upload if desired. -3. Optionally re-run evidence for the exact software version named in Availability (v1.0.0 packaging vs newer source tag) and update Table 1. -4. Deposit preprint (bioRxiv) and/or archive DOI (Zenodo) if desired before or with submission. -5. Submit format-free PDF + SI; on revision, switch to OUP General Template. -6. Confirm APC / Read & Publish / ISCB discount before acceptance. +1. Confirm Jian-Feng Mao co-authorship for FastaGuard is intentional (defaulted yes per user). +2. Optional: archive a FastaGuard release on Zenodo and add DOI to Availability. +3. Optional preprint (bioRxiv); recount words after any edits. +4. Submit format-free PDF; on revision use OUP General Template. +5. Confirm APC / Read & Publish / ISCB discount before acceptance. ## Key in-repo sources for claims - `README.md`, `docs/product-thesis.md`, `docs/tool-landscape.md`, `docs/output-contract.md` - `docs/evidence/fastaguard-v0.6-public-evidence.md` + `docs/evidence/results/v0.6/` -- `CITATION.cff` +- VariantFlow front-matter only: `https://github.com/ehsanestaji/VariantFlow/tree/main/paper/bioinformatics-application-note` diff --git a/paper/README.md b/paper/README.md index 8ea3e67..5f9bb74 100644 --- a/paper/README.md +++ b/paper/README.md @@ -50,8 +50,9 @@ Overleaf: upload `main.tex`, `references.bib`, and compile with pdfLaTeX. ## Draft status (short) -- **Done:** full draft body, TikZ overview figure, evidence table from v0.6 public pack, BibTeX, build path. -- **Still TBD before submission:** author email/affiliation, funding, optional version-frozen re-evidence, preprint/DOI, OUP template on revision. +- **Done:** full draft body; TikZ overview figure; v0.6 evidence table; BibTeX; build path. +- **Front-matter:** author/ORCID/Umeå/funding/COI/AI/CRediT reused from VariantFlow Application Note (see `HANDOFF.md`) — scientific content stays FastaGuard-only. +- **Still TBD:** FastaGuard Zenodo/preprint DOI, OUP template on revision, ScholarOne submit. - **Framing rule:** never pitch as “FastQC for FASTA.” Durable venue + project context for multi-agent handoff live in the Project store: diff --git a/paper/main.tex b/paper/main.tex index 1533358..05f76d9 100644 --- a/paper/main.tex +++ b/paper/main.tex @@ -1,79 +1,90 @@ % FastaGuard — Bioinformatics (OUP) Application Note % USER-CONFIRMED venue: Application Note (~4 pages / ~2,000–2,600 words) -% Format-free first submission OK; use OUP General Template for revision. +% Author / funding / disclosure front-matter aligned with VariantFlow +% (https://github.com/ehsanestaji/VariantFlow paper/bioinformatics-application-note/). +% Scientific claims and evidence remain FastaGuard-specific (docs/evidence/). % Build: make (see README.md and HANDOFF.md) -% -% Status: full first draft for review — author email/affiliation/funding still TBD; -% TikZ figure is submission-usable; table numbers from committed v0.6 evidence. \pdfminorversion=5 \documentclass[11pt,a4paper]{article} % Draft layout approximating journal constraints without requiring oup-authoring-template. % For camera-ready / revision, switch to the OUP General Template with Bioinformatics % options: \documentclass[numsec,webpdf,modern,large]{...} and author-date bib style. -\usepackage[margin=2.5cm]{geometry} +\usepackage[margin=1in]{geometry} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} \usepackage{times} \usepackage{graphicx} \usepackage{booktabs} +\usepackage{array} \usepackage{hyperref} \usepackage{xcolor} \usepackage{natbib} \usepackage{url} -\usepackage{setspace} +\usepackage{microtype} \usepackage{lineno} +\usepackage{amssymb} \usepackage{tikz} \usetikzlibrary{arrows.meta,positioning,fit,calc} +\definecolor{fgblue}{HTML}{1F4E79} + \hypersetup{ colorlinks=true, - linkcolor=black, - citecolor=blue!50!black, - urlcolor=blue!50!black + linkcolor=fgblue, + citecolor=fgblue, + urlcolor=fgblue } - -% Single spacing approximates final page budget while drafting. -% For ScholarOne first submission, enable \doublespacing and keep \linenumbers. -% \doublespacing -\linenumbers +\setlength{\emergencystretch}{2em} \title{FastaGuard: FASTA preflight QC for modern bioinformatics pipelines} - \author{ - Ehsan Estaji% - \thanks{Corresponding author. Email: \texttt{[institutional-email@example.org]}}% - \\ - \textit{[Affiliation to be completed]} +Ehsan Estaji\textsuperscript{1,$\dag$} +\href{https://orcid.org/0009-0005-0838-228X}{\small(ORCID 0009-0005-0838-228X)} \qquad +Jian-Feng Mao\textsuperscript{1,$\dag$} +\href{https://orcid.org/0000-0001-9735-8516}{\small(ORCID 0000-0001-9735-8516)} \\[6pt] +\small \textsuperscript{1}Ume{\aa} Plant Science Centre, +Department of Plant Physiology, Ume{\aa} University, +SE-901\,87 Ume{\aa}, Sweden \\[4pt] +\small \textsuperscript{$\dag$}Correspondence: + \href{mailto:ehsan.estaji@umu.se}{\texttt{ehsan.estaji@umu.se}}, + \href{mailto:jianfeng.mao@umu.se}{\texttt{jianfeng.mao@umu.se}} } - \date{} \begin{document} +\linenumbers \maketitle \begin{abstract} \noindent\textbf{Summary:} FastaGuard is a fast, explainable FASTA preflight QC tool that validates assembly FASTA files, detects structural and composition red flags, and emits a stable -multi-format report contract before expensive downstream analysis. +multi-format report contract before expensive downstream analysis such as QUAST, +BUSCO, BlobToolKit, CheckM, annotation, or official repository validation. \noindent\textbf{Availability and Implementation:} -Freely available under the MIT license at +Source code and documentation are freely available under the MIT license at \url{https://github.com/ehsanestaji/FastaGuard}. -Install via Bioconda (\texttt{mamba install -c bioconda fastaguard}), BioContainers, -or GitHub release binaries. Implemented in Rust; supported on Linux and macOS. -Published packaging targets include Bioconda builds for \texttt{linux-64}, -\texttt{linux-aarch64}, \texttt{osx-64}, and \texttt{osx-arm64}, plus a pinned -BioContainers image. +Install via Bioconda (\texttt{mamba install -c conda-forge -c bioconda fastaguard}), +BioContainers, or GitHub release binaries. +Implemented in Rust; supported on Linux and macOS, including Bioconda builds for +\texttt{linux-64}, \texttt{linux-aarch64}, \texttt{osx-64}, and \texttt{osx-arm64}. \noindent\textbf{Contact:} -\texttt{[institutional-email@example.org]} +\href{mailto:ehsan.estaji@umu.se}{\texttt{ehsan.estaji@umu.se}}; +\href{mailto:jianfeng.mao@umu.se}{\texttt{jianfeng.mao@umu.se}} \noindent\textbf{Supplementary information:} -Supplementary notes, finding-catalog excerpts, and reproducibility pointers are -maintained with the software repository under \texttt{docs/} and \texttt{paper/}. +Finding-catalog excerpts, evidence packs, and reproducibility notes are maintained +with the software repository under \texttt{docs/} and \texttt{paper/}. \end{abstract} -\section*{Motivation} +\vspace{0.4em} +\noindent\textbf{Keywords:} FASTA, quality control, genome assembly, preflight, +bioinformatics pipelines, Rust + +\section{Introduction} Genome assemblies and reference FASTA files are foundational inputs for bioinformatics pipelines, yet FASTA-level quality control remains fragmented. Teams commonly combine ad hoc scripts, general sequence utilities such as @@ -114,7 +125,7 @@ \section*{Motivation} stable field names, documented finding identifiers, provenance checksums, and gate continuation booleans that pipelines can depend on across releases. -\section*{Implementation} +\section{Implementation} FastaGuard is a streaming-first command-line tool implemented in Rust and distributed as a single binary. The primary assembly workflow is: @@ -162,7 +173,7 @@ \section*{Implementation} \texttt{machine\_summary.safe\_for\_downstream} is intentionally stricter (true only for overall PASS) and is not a synonym for gate continuation. -\paragraph{Submission-readiness and compare mode.} +\paragraph{Submission-readiness, compare mode, and reference checks.} \texttt{--gate submission} with \texttt{--submission-target generic|ncbi} performs FASTA-level submission preflight before official validators. For NCBI genome targeting, FastaGuard applies a documented @@ -237,7 +248,7 @@ \section*{Implementation} \label{fig:overview} \end{figure} -\section*{Results} +\section{Results} Committed public-evidence summaries for FastaGuard v0.6.0 illustrate the intended behaviour on synthetic contract fixtures and two exact NCBI reference assemblies (Table~\ref{tab:evidence}). @@ -268,14 +279,15 @@ \section*{Results} Compare mode and submission gates extend the same contract to cohort triage and FASTA-level submission hazards; reference mode adds companion-declaration compatibility checks when indexes, dictionaries, or headers must agree with a -canonical FASTA before mapping or variant workflows proceed. +canonical FASTA before mapping or annotation workflows proceed. \begin{table}[t] \centering \caption{Illustrative FastaGuard v0.6.0 public-evidence outcomes (assembly profile, pipeline gate). Elapsed times are single-machine contextual measurements from the committed evidence pack, not cross-platform guarantees. - Full portable JSON/TSV summaries are in the software repository.} + Full portable JSON/TSV summaries are in the software repository + (\texttt{docs/evidence/}).} \label{tab:evidence} \begin{tabular}{@{}llllr@{}} \toprule @@ -304,7 +316,7 @@ \section*{Results} That boundary is deliberate: FastaGuard owns FASTA-level preflight and triage; interpretive tools own interpretive questions. -\section*{Discussion} +\section{Discussion} FastaGuard's contribution is a pipeline-native FASTA preflight \emph{contract}: streaming validation, explainable findings, stable schemas, explicit gates, and packaging paths that workflow authors can adopt without reinventing per-pipeline @@ -328,14 +340,41 @@ \section*{Discussion} releases and, if warranted later, a separate research article rather than an overloaded note. -\section*{Acknowledgements} -Funding: [to be completed]. -Conflict of interest: none declared. -The software repository acknowledgements and release notes credit packaging and -workflow integration milestones (Bioconda, BioContainers, nf-core modules, and -Snakemake wrappers). +\section*{Author contributions} +Following CRediT, E.E.: conceptualisation, software, validation, investigation, +data curation, and writing of the original draft. J.-F.M.: supervision and +writing through review and editing. Both authors read and approved the final +manuscript. + +\section*{Funding} +This work was partially supported by the Wallenberg Initiatives in Forest +Research (WIFORCE) funded by the Knut and Alice Wallenberg Foundation. -\bibliographystyle{plainnat} +\section*{Conflict of interest} +The authors declare no competing interests. + +\section*{AI usage disclosure} +AI coding assistants (including Cursor-based agents and related large-language-model +tools) were used for planning, manuscript scaffolding, repository evidence +cross-checks, and prose drafting support. +The authors reviewed all output and remain responsible for the manuscript, code, +evidence interpretation, and all scientific claims. + +\section*{Acknowledgements} +Computing resources provided by SNIC/HPC2N at Ume{\aa} University. +We thank packaging and workflow maintainers who exercised Bioconda, +BioContainers, nf-core module, and Snakemake wrapper integrations for FastaGuard. + +\section*{Data availability} +FastaGuard source code, schemas, finding catalog, and documentation are openly +available at \url{https://github.com/ehsanestaji/FastaGuard} under the MIT +license. +Committed public-evidence summaries used in Table~\ref{tab:evidence} are +archived under \texttt{docs/evidence/} in the repository. +No new biological samples were generated for this study; public NCBI assemblies +cited in the evidence pack are used under their existing data-access terms. + +\bibliographystyle{apalike} \bibliography{references} \end{document} From 79692657f90208c9baf3b5180587bc4bb36b394c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 24 Oct 2026 14:57:37 +0000 Subject: [PATCH 4/9] Add Application Note submission control package and evidence freeze Prepare format-free Bioinformatics submission: submission.yml, check script, paper-readiness checklist, cover letter, Zenodo/bioRxiv prep without fake DOIs, pin Availability to v1.0.0 and Table 1 to the v0.6 evidence TSV. Signed-off-by: Cursor Agent Co-authored-by: ehsan.estaji --- .gitignore | 1 + .zenodo.json | 36 +++++++ docs/paper-readiness.md | 54 ++++++++++ paper/HANDOFF.md | 97 +++++------------ paper/Makefile | 15 ++- paper/README.md | 74 +++++-------- paper/ZENODO.md | 33 ++++++ paper/biorxiv-checklist.md | 34 ++++++ paper/check_submission.py | 206 +++++++++++++++++++++++++++++++++++++ paper/cover-letter.md | 42 ++++++++ paper/main.tex | 42 ++++---- paper/references.bib | 4 +- paper/submission.yml | 109 ++++++++++++++++++++ 13 files changed, 604 insertions(+), 143 deletions(-) create mode 100644 .zenodo.json create mode 100644 docs/paper-readiness.md create mode 100644 paper/ZENODO.md create mode 100644 paper/biorxiv-checklist.md create mode 100644 paper/check_submission.py create mode 100644 paper/cover-letter.md create mode 100644 paper/submission.yml diff --git a/.gitignore b/.gitignore index 60c2626..7fed34e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ paper/*.toc paper/*.lof paper/*.lot paper/*.pdf +paper/build/ diff --git a/.zenodo.json b/.zenodo.json new file mode 100644 index 0000000..48d476e --- /dev/null +++ b/.zenodo.json @@ -0,0 +1,36 @@ +{ + "title": "FastaGuard: FASTA preflight QC for modern bioinformatics pipelines", + "description": "FastaGuard is a streaming-first Rust CLI for FASTA preflight quality control in assembly pipelines. It validates structure, flags FASTA-level red flags, and emits a stable HTML/JSON/TSV/MultiQC report contract with explicit pipeline gates before expensive downstream QC (QUAST, BUSCO, BlobToolKit, CheckM, annotation, or official validators). This Zenodo deposit archives a citable software release accompanying the Bioinformatics Application Note.", + "upload_type": "software", + "access_right": "open", + "license": "MIT", + "creators": [ + { + "name": "Estaji, Ehsan", + "orcid": "0009-0005-0838-228X", + "affiliation": "Umeå Plant Science Centre, Department of Plant Physiology, Umeå University" + }, + { + "name": "Mao, Jian-Feng", + "orcid": "0000-0001-9735-8516", + "affiliation": "Umeå Plant Science Centre, Department of Plant Physiology, Umeå University" + } + ], + "keywords": [ + "FASTA", + "quality control", + "genome assembly", + "preflight", + "bioinformatics", + "Rust", + "pipelines" + ], + "related_identifiers": [ + { + "identifier": "https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0", + "relation": "isSupplementTo", + "resource_type": "software" + } + ], + "notes": "Deposit the existing GitHub release tag v1.0.0 (Reference Contract Gate / Bioconda-published line). Do not invent DOIs. After Zenodo mints a version DOI, paste it into paper/submission.yml and the Availability sentence in paper/main.tex, then rebuild the PDF." +} diff --git a/docs/paper-readiness.md b/docs/paper-readiness.md new file mode 100644 index 0000000..feadac0 --- /dev/null +++ b/docs/paper-readiness.md @@ -0,0 +1,54 @@ +# FastaGuard paper readiness (*Bioinformatics* Application Note) + +Venue: **Bioinformatics (OUP) — Application Note** (user-confirmed). +Branch/PR: `cursor/paper-prep-venues-198e` / [#21](https://github.com/ehsanestaji/FastaGuard/pull/21). +Control manifest: [`paper/submission.yml`](../paper/submission.yml). +Machine check: `cd paper && make check`. + +## DONE (machine / repo complete) + +| Item | Status | +| --- | --- | +| Full Application Note draft (`paper/main.tex`) | DONE | +| Structured abstract (Summary / Availability / Contact / SI) | DONE | +| Authors, ORCID, Umeå affiliation, emails (from VariantFlow front-matter) | DONE | +| Funding (WIFORCE/KAW), COI, AI disclosure, CRediT, acknowledgements | DONE | +| Evidence freeze: Table 1 numbers match `docs/evidence/results/v0.6/evidence_summary.tsv` | DONE | +| Cited software release for Availability: GitHub **`v1.0.0`** (Bioconda-published) | DONE | +| Forbidden phrase guard (“FastQC for FASTA”) in `check_submission.py` | DONE | +| `.zenodo.json` metadata prepared (no fake DOI) | DONE | +| Cover letter draft (`paper/cover-letter.md`) | DONE | +| bioRxiv build target + checklist | DONE | +| PDF builds (`make`) | DONE | +| Word count ~2.0–2.6k band | DONE (~2.0k+) | + +## USER-ACTION (requires Ehsan / credentials / co-author) + +| Item | Notes | +| --- | --- | +| Co-author final OK (Jian-Feng Mao) | Confirm authorship/order/CRediT | +| Zenodo DOI from existing `v1.0.0` release | Follow [`paper/ZENODO.md`](../paper/ZENODO.md); then paste DOI into Availability + `submission.yml` and rebuild | +| Optional bioRxiv deposit | Follow [`paper/biorxiv-checklist.md`](../paper/biorxiv-checklist.md) | +| ScholarOne upload | Format-free first submission OK; upload PDF + cover letter; OUP General Template deferred to **revision** | +| APC / Read & Publish / ISCB discount | Check before acceptance | +| Suggested editors (optional) | Cover letter suggests assembly/sequence-analysis tools area | + +## Pre-submit command checklist + +```bash +cd paper +make check # must exit 0 +make # builds main.pdf +make biorxiv # optional preprint PDF copy +``` + +## Evidence vs software versions + +- **Availability / packaging cite:** FastaGuard **`v1.0.0`** + (). +- **Table 1 evidence freeze:** committed **v0.6.0** public-evidence pack + (`docs/evidence/…`); timings are contextual contract illustrations, not a + claim that v1.0.0 was re-benchmarked on that host. +- Do **not** invent a new tag solely for the paper; `v1.0.0` already exists. + Latest GitHub “Latest” may be newer (e.g. v1.1.0) — paper cites v1.0.0 as the + Bioconda reference-contract release unless Availability is intentionally retargeted. diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md index c806db3..60515e0 100644 --- a/paper/HANDOFF.md +++ b/paper/HANDOFF.md @@ -1,90 +1,43 @@ # Paper track handoff (cold-start) -For any agent or human resuming FastaGuard **paper preparation** after a pause. -This track is **paper-only**. Do not change product CLI/schema/packaging unless -the user explicitly expands scope. Product/dev work may be on a separate track -— do not conflict with it. +Paper-only track. Do not change product CLI/schema/packaging unless asked. +Venue: ***Bioinformatics* (OUP) Application Note** — **user-confirmed**. +PR: [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · branch `cursor/paper-prep-venues-198e`. -## Locked decisions +## Machine-ready vs USER-ACTION -| Item | Value | +| Status | Items | | --- | --- | -| Venue | ***Bioinformatics* (OUP) — Application Note** (**user-confirmed**) | -| Working title | `FastaGuard: FASTA preflight QC for modern bioinformatics pipelines` | -| Framing | FASTA **preflight** contract before QUAST/BUSCO/BlobToolKit/etc. | -| Forbidden phrase | Do **not** use “FastQC for FASTA” | -| Repo branch / PR | `cursor/paper-prep-venues-198e` / [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) | -| LaTeX root | `paper/main.tex` | -| Author / funding template source | [VariantFlow](https://github.com/ehsanestaji/VariantFlow) `paper/bioinformatics-application-note/` (**user-directed reuse**) | +| **DONE / submit-package ready** | Full draft; front-matter; evidence freeze; `submission.yml`; `check_submission.py`; cover letter; `.zenodo.json`; bioRxiv build target; `docs/paper-readiness.md` | +| **USER-ACTION only** | Co-author final OK; Zenodo DOI mint (`paper/ZENODO.md`); optional bioRxiv; ScholarOne upload; APC/R&P/ISCB | -## What was copied from VariantFlow (front-matter only) +Do **not** claim ScholarOne submitted from this agent track. -Reuse these from VariantFlow’s Application Note — **not** its VCF science: - -- Author list: Ehsan Estaji + Jian-Feng Mao, ORCIDs, Umeå Plant Science Centre affiliation -- Correspondence emails: `ehsan.estaji@umu.se`, `jianfeng.mao@umu.se` -- CRediT **Author contributions** wording pattern -- **Funding:** WIFORCE / Knut and Alice Wallenberg Foundation -- **Conflict of interest:** no competing interests -- **AI usage disclosure** pattern (adapted to FastaGuard / Cursor agents) -- **Acknowledgements:** SNIC/HPC2N at Umeå University (+ FastaGuard packaging thanks) -- Sectioning: Introduction / Implementation / Results / Discussion + end-matter blocks -- LaTeX front-matter conventions (margin, ORCID hyperlinks, `apalike`, keywords line) - -## What must stay FastaGuard-specific - -- All scientific claims, comparison framing, gates/contract description -- Evidence table and timings from `docs/evidence/` (v0.6 public pack) — **never** VariantFlow VCF benchmarks/tables -- Availability URLs, Bioconda/BioContainers/nf-core/Snakemake packaging story -- TikZ preflight figure and FastaGuard `references.bib` (QUAST/BUSCO/etc.) - -## What is done - -- Full Application Note draft with VariantFlow-aligned author/funding/disclosures -- Structured Bioinformatics Application Note abstract (Summary / Availability / Contact / SI) -- TikZ overview figure; v0.6 evidence table -- ~2.0k words (within ~2.0–2.6k Application Note target); PDF builds via `make` -- Store context + venue brief updated - -## Draft vs final - -| Piece | Status | -| --- | --- | -| Author / ORCID / affiliation / emails | **Filled from VariantFlow** (same author block by user default) | -| Funding / COI / AI disclosure / CRediT | **Filled** (VariantFlow patterns) | -| Scientific body + evidence | **FastaGuard draft-complete** | -| Word count | ~2.0k — OK; trim if journal typesetting overruns | -| Figure | Usable TikZ | -| Table | v0.6 evidence (contextual timings) | -| Zenodo / preprint DOI | **Not yet** (VariantFlow had these; add for FastaGuard when archived) | -| OUP camera-ready template | **Not yet** — format-free OK for first submission | -| ScholarOne submit | **Not yet** | - -## How to build +## Commands ```bash cd paper -make # → main.pdf -make clean +make check # must exit 0 +make # main.pdf +make biorxiv # build/fastaguard-biorxiv.pdf ``` -Requires TeX Live with `pdflatex`, `bibtex`, `latexmk`, TikZ (`texlive-pictures`), `microtype`. +## Freezes + +- **Software Availability cite:** GitHub **`v1.0.0`** (Bioconda `fastaguard=1.0.0`) +- **Table 1 evidence:** `docs/evidence/results/v0.6/evidence_summary.tsv` (fastaguard **0.6.0**, commit `cf27295…`) +- **Forbidden phrase:** never use “FastQC for FASTA” affirmatively -## Store docs +## Copied from VariantFlow (front-matter only) -- `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/publication-venues.md` -- `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` +Authors/ORCID/Umeå/emails; WIFORCE funding; COI; AI disclosure; CRediT; SNIC ack; sectioning conventions. -## Next concrete steps +## Not copied -1. Confirm Jian-Feng Mao co-authorship for FastaGuard is intentional (defaulted yes per user). -2. Optional: archive a FastaGuard release on Zenodo and add DOI to Availability. -3. Optional preprint (bioRxiv); recount words after any edits. -4. Submit format-free PDF; on revision use OUP General Template. -5. Confirm APC / Read & Publish / ISCB discount before acceptance. +VariantFlow VCF claims, benchmarks, tables, Zenodo/bioRxiv DOIs. -## Key in-repo sources for claims +## Key paths -- `README.md`, `docs/product-thesis.md`, `docs/tool-landscape.md`, `docs/output-contract.md` -- `docs/evidence/fastaguard-v0.6-public-evidence.md` + `docs/evidence/results/v0.6/` -- VariantFlow front-matter only: `https://github.com/ehsanestaji/VariantFlow/tree/main/paper/bioinformatics-application-note` +- Manifest: `paper/submission.yml` +- Readiness: `docs/paper-readiness.md` +- Store context: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` diff --git a/paper/Makefile b/paper/Makefile index cbd80c2..f452b3e 100644 --- a/paper/Makefile +++ b/paper/Makefile @@ -1,14 +1,24 @@ -# Build the FastaGuard Application Note PDF with a standard TeX toolchain. +# Build and validate the FastaGuard Application Note PDF. MAIN := main LATEXMK ?= latexmk +BUILDDIR := build +BIORXIV_PDF := $(BUILDDIR)/fastaguard-biorxiv.pdf -.PHONY: all pdf clean distclean view +.PHONY: all pdf check biorxiv clean distclean view all: pdf pdf: $(LATEXMK) -pdf -interaction=nonstopmode -halt-on-error $(MAIN).tex +check: + python3 check_submission.py + +biorxiv: pdf + mkdir -p $(BUILDDIR) + cp $(MAIN).pdf $(BIORXIV_PDF) + @echo "Wrote $(BIORXIV_PDF)" + # Fallback if latexmk is unavailable: pdflatex-only: pdflatex -interaction=nonstopmode $(MAIN).tex @@ -22,6 +32,7 @@ clean: distclean: clean rm -f $(MAIN).pdf + rm -rf $(BUILDDIR) view: pdf @echo "Built $(MAIN).pdf" diff --git a/paper/README.md b/paper/README.md index 5f9bb74..5b14711 100644 --- a/paper/README.md +++ b/paper/README.md @@ -1,61 +1,39 @@ # FastaGuard paper (LaTeX) -**Venue (user-confirmed):** [*Bioinformatics*](https://academic.oup.com/bioinformatics) (Oxford Academic) — **Application Note**. +**Venue (user-confirmed):** [*Bioinformatics*](https://academic.oup.com/bioinformatics) — **Application Note** +**PR:** [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · `cursor/paper-prep-venues-198e` -**PR / branch:** [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · `cursor/paper-prep-venues-198e` +Paper-prep only. Cold-start: [`HANDOFF.md`](HANDOFF.md). Readiness checklist: [`../docs/paper-readiness.md`](../docs/paper-readiness.md). -This folder is **paper preparation only**. It does not change the FastaGuard product. -Cold-start resume details: see [`HANDOFF.md`](HANDOFF.md). - -## Article constraints - -| Item | Target | -| --- | --- | -| Type | Application Note | -| Length | Up to ~4 pages (~2,600 words, or ~2,000 words + one figure) | -| Title | Must include the application name (`FastaGuard: …`) | -| Abstract | Summary / Availability and Implementation / Contact / Supplementary information | -| Software | Freely available; state availability + implementation; keep available ≥2 years | - -Format-free initial submission is allowed. For revision / camera-ready, switch to the [OUP General Template](https://www.overleaf.com/latex/templates/oup-general-template/fqkhysbcbpwv) with Bioinformatics options (`numsec,webpdf,modern,large`) and author-date bibliography style as documented by OUP. - -## Layout - -```text -paper/ - main.tex Full Application Note draft - references.bib BibTeX database - Makefile latexmk / pdflatex build - README.md this file - HANDOFF.md cold-start status + next steps - figures/ notes for optional external artwork - tables/ optional CSV / working tables -``` - -## Build the PDF - -Requires TeX Live (or MacTeX / MiKTeX) with `pdflatex`, `bibtex`, `latexmk`, plus -`geometry`, `times`, `graphicx`, `booktabs`, `hyperref`, `xcolor`, `natbib`, -`setspace`, `lineno`, `url`, and **TikZ** (`texlive-pictures`). +## Build & check ```bash cd paper -make # → main.pdf -make pdflatex-only -make clean # keep PDF -make distclean # remove PDF too +make check # submission control (must pass) +make # → main.pdf +make biorxiv # → build/fastaguard-biorxiv.pdf ``` -Overleaf: upload `main.tex`, `references.bib`, and compile with pdfLaTeX. +Requires TeX Live (`pdflatex`, `bibtex`, `latexmk`, TikZ, `microtype`) and Python 3. + +## Submission package files + +| File | Role | +| --- | --- | +| `main.tex` / `references.bib` | Manuscript | +| `submission.yml` | Control manifest (versions, freezes, blockers) | +| `check_submission.py` | Machine gate | +| `cover-letter.md` | ScholarOne cover letter draft | +| `ZENODO.md` | USER-ACTION DOI mint from `v1.0.0` | +| `biorxiv-checklist.md` | Optional preprint steps | +| `../.zenodo.json` | Zenodo metadata (no DOI invented) | -## Draft status (short) +## Freezes -- **Done:** full draft body; TikZ overview figure; v0.6 evidence table; BibTeX; build path. -- **Front-matter:** author/ORCID/Umeå/funding/COI/AI/CRediT reused from VariantFlow Application Note (see `HANDOFF.md`) — scientific content stays FastaGuard-only. -- **Still TBD:** FastaGuard Zenodo/preprint DOI, OUP template on revision, ScholarOne submit. -- **Framing rule:** never pitch as “FastQC for FASTA.” +- Availability software: **`v1.0.0`** +- Table 1 evidence: **v0.6.0** pack under `docs/evidence/results/v0.6/` +- Format-free first submission OK; OUP General Template deferred to revision -Durable venue + project context for multi-agent handoff live in the Project store: +## Framing -- `docs/publication-venues.md` -- `docs/project-context.md` +Never pitch as “FastQC for FASTA.” diff --git a/paper/ZENODO.md b/paper/ZENODO.md new file mode 100644 index 0000000..a991053 --- /dev/null +++ b/paper/ZENODO.md @@ -0,0 +1,33 @@ +# Zenodo archive steps (USER-ACTION) + +No Zenodo DOI is invented in the manuscript. GitHub release **`v1.0.0`** already +exists and is the cited Availability / Bioconda line +(). + +## Mint a DOI from the existing release + +1. Sign in to [Zenodo](https://zenodo.org/) with an account that can access the + `ehsanestaji/FastaGuard` GitHub repository (enable the Zenodo GitHub + integration under Zenodo → GitHub if needed). +2. Enable the **FastaGuard** repository in Zenodo’s GitHub listing. +3. Prefer depositing the **existing** `v1.0.0` release (do not invent a new + version number for the paper). Options: + - If Zenodo is linked: create a *new* GitHub release only if you intentionally + want a fresh archive tag; otherwise use Zenodo’s UI to refresh/archive the + `v1.0.0` release asset / tag already published. + - Or upload the `v1.0.0` source tarball from GitHub releases manually and + apply metadata from repo-root [`.zenodo.json`](../.zenodo.json). +4. Publish the Zenodo record. Copy: + - **Concept DOI** (optional, always resolves to latest) + - **Version DOI** for `v1.0.0` (preferred for Availability) +5. Paste into: + - `paper/submission.yml` → `release.archive_doi` / `release.version_doi` + - `paper/main.tex` Availability sentence (add + `\href{https://doi.org/10.5281/zenodo.XXXX}{doi:10.5281/zenodo.XXXX}`) +6. Rebuild: `cd paper && make && make check` + +## Do not + +- Invent or placeholder a `10.5281/zenodo.…` DOI in the submit-ready PDF. +- Retag a different software version unless the manuscript Availability is + updated to match. diff --git a/paper/biorxiv-checklist.md b/paper/biorxiv-checklist.md new file mode 100644 index 0000000..7db6c2a --- /dev/null +++ b/paper/biorxiv-checklist.md @@ -0,0 +1,34 @@ +# bioRxiv upload checklist (USER-ACTION, optional) + +Prepare a preprint **before or with** ScholarOne submission. OUP allows +preprints; update the preprint with the published DOI after acceptance. + +## Build the bioRxiv-ready PDF + +```bash +cd paper +make biorxiv +# → paper/build/fastaguard-biorxiv.pdf +``` + +This is the same Application Note PDF with a stable filename for upload. + +## Upload steps + +1. Create/sign in to a bioRxiv account. +2. Start a new preprint in **Bioinformatics** / computational biology category + (choose the closest bioRxiv subject collection). +3. Upload `paper/build/fastaguard-biorxiv.pdf`. +4. Title: `FastaGuard: FASTA preflight QC for modern bioinformatics pipelines` +5. Authors / affiliations / ORCIDs as in `paper/main.tex`. +6. Abstract: paste the structured Summary paragraph (bioRxiv may want a single + abstract block — use the Summary text; keep Availability as a separate + statement in the PDF). +7. Confirm license compatible with later OUP CC BY publication. +8. After posting, record the bioRxiv DOI in `paper/submission.yml` notes and + optionally in Acknowledgements / Data availability. + +## Do not + +- Claim the preprint is posted until the bioRxiv confirmation page shows a DOI. +- Put a fake bioRxiv DOI in `main.tex`. diff --git a/paper/check_submission.py b/paper/check_submission.py new file mode 100644 index 0000000..68f7f12 --- /dev/null +++ b/paper/check_submission.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Validate the FastaGuard Bioinformatics Application Note submission package. + +Fails (exit 1) on manuscript TODOs/placeholders, forbidden framing, missing +Availability URLs, missing author emails, or evidence-table drift vs the frozen +v0.6 public-evidence TSV. Zenodo DOI absence is reported as a human blocker, +not a machine failure (no fake DOIs). + +Usage: + python3 paper/check_submission.py + make -C paper check +""" + +from __future__ import annotations + +import csv +import re +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PAPER = ROOT / "paper" +MANIFEST = PAPER / "submission.yml" +MANUSCRIPT = PAPER / "main.tex" +EVIDENCE_TSV = ROOT / "docs" / "evidence" / "results" / "v0.6" / "evidence_summary.tsv" +FORBIDDEN = "FastQC for FASTA" +PLACEHOLDER_RES = ( + re.compile(r"\bTODO\b"), + re.compile(r"\bTBD\b"), + re.compile(r"\[institutional-email@", re.I), + re.compile(r"\[Affiliation to be completed\]", re.I), + re.compile(r"Funding:\s*\[to be completed\]", re.I), + re.compile(r"example\.org", re.I), +) + + +def read(path: Path) -> str: + if not path.exists(): + raise SystemExit(f"ERROR: missing required file: {path.relative_to(ROOT)}") + return path.read_text(encoding="utf-8") + + +def require_tokens(label: str, text: str, tokens: tuple[str, ...]) -> list[str]: + return [f"{label} missing {token!r}" for token in tokens if token not in text] + + +def manifest_list(manifest: str, section: str) -> list[str]: + pattern = rf"^{re.escape(section)}:\n((?: - .+\n)+)" + match = re.search(pattern, manifest, flags=re.MULTILINE) + if not match: + return [] + return [line.strip()[2:] for line in match.group(1).splitlines() if line.strip().startswith("- ")] + + +def load_evidence_rows() -> dict[str, dict[str, str]]: + with EVIDENCE_TSV.open(encoding="utf-8") as handle: + reader = csv.DictReader(handle, delimiter="\t") + return {row["id"]: row for row in reader} + + +def main() -> int: + errors: list[str] = [] + warnings: list[str] = [] + human_blockers: list[str] = [] + + manifest = read(MANIFEST) + manuscript = read(MANUSCRIPT) + + errors += require_tokens( + "paper/submission.yml", + manifest, + ( + "target_journal: Bioinformatics", + "article_type: Application Note", + "tag: v1.0.0", + "fastaguard_version: \"0.6.0\"", + "forbidden_phrase: \"FastQC for FASTA\"", + "performance_claim_source: docs/evidence_only", + "ehsan.estaji@umu.se", + "jianfeng.mao@umu.se", + ), + ) + + errors += require_tokens( + "paper/main.tex", + manuscript, + ( + r"\begin{abstract}", + r"\textbf{Summary:}", + r"\textbf{Availability and Implementation:}", + r"\textbf{Contact:}", + r"\textbf{Supplementary information:}", + "https://github.com/ehsanestaji/FastaGuard", + "ehsan.estaji@umu.se", + "jianfeng.mao@umu.se", + "0009-0005-0838-228X", + "0000-0001-9735-8516", + r"\section*{Funding}", + r"\section*{Conflict of interest}", + r"\section*{AI usage disclosure}", + "WIFORCE", + "v1.0.0", + "0.0194", + "0.0185", + "0.0569", + "0.3441", + "GCF\\_000005845.2", + "GCF\\_000182925.2", + ), + ) + + if FORBIDDEN.lower() in manuscript.lower(): + # Allow negation contexts that explicitly reject the phrase. + for match in re.finditer(re.escape(FORBIDDEN), manuscript, flags=re.IGNORECASE): + window = manuscript[max(0, match.start() - 40) : match.end() + 10].lower() + if "not" in window or "never" in window or "avoid" in window or "rather than" in window: + continue + errors.append(f"paper/main.tex contains forbidden phrase {FORBIDDEN!r}") + break + + for pattern in PLACEHOLDER_RES: + if pattern.search(manuscript): + errors.append(f"paper/main.tex still has placeholder matching {pattern.pattern}") + + # Evidence freeze: every table timing/accession must match the TSV. + if not EVIDENCE_TSV.exists(): + errors.append(f"missing evidence freeze file: {EVIDENCE_TSV.relative_to(ROOT)}") + else: + rows = load_evidence_rows() + expected = { + "synthetic_valid": ("0.0194", "PASS", "604", "4"), + "problem_fixture": ("0.0185", "FAIL", "145", "5"), + "ecoli_k12_mg1655": ("0.0569", "PASS", "4641652", "1"), + "neurospora_crassa_or74a": ("0.3441", "WARN", "41102378", "21"), + } + for row_id, (elapsed, verdict, length, nseq) in expected.items(): + row = rows.get(row_id) + if not row: + errors.append(f"evidence TSV missing row id={row_id}") + continue + if row["elapsed_seconds"] != elapsed: + errors.append( + f"evidence drift for {row_id}: elapsed_seconds={row['elapsed_seconds']!r} != {elapsed!r}" + ) + if row["verdict"] != verdict or row["gate_status"] != verdict: + errors.append( + f"evidence drift for {row_id}: verdict/gate={row['verdict']}/{row['gate_status']}" + ) + if row["total_length"] != length or row["sequence_count"] != nseq: + errors.append(f"evidence drift for {row_id}: length/count mismatch") + if elapsed not in manuscript: + errors.append(f"paper/main.tex missing frozen elapsed value {elapsed} for {row_id}") + + for rel in manifest_list(manifest, "submission_docs"): + path = ROOT / rel + if not path.exists(): + errors.append(f"manifest path does not exist: {rel}") + + for required in ( + PAPER / "cover-letter.md", + PAPER / "ZENODO.md", + PAPER / "biorxiv-checklist.md", + ROOT / "docs" / "paper-readiness.md", + ROOT / ".zenodo.json", + ): + if not required.exists(): + errors.append(f"missing required submission file: {required.relative_to(ROOT)}") + + # Rough word-count band for Application Note body. + body = re.sub(r"\\begin\{tikzpicture\}.*?\\end\{tikzpicture\}", " ", manuscript, flags=re.S) + body = re.sub(r"\\begin\{verbatim\}.*?\\end\{verbatim\}", " ", body, flags=re.S) + plain = re.sub(r"\\[a-zA-Z]+\*?(?:\[[^\]]*\])?", " ", body) + plain = re.sub(r"[{}]", " ", plain) + words = re.findall(r"[A-Za-z0-9']+", plain) + n_words = len(words) + if n_words < 1800 or n_words > 3200: + warnings.append( + f"approximate manuscript token count {n_words} outside soft band 1800–3200 " + "(Application Note target ~2000–2600 words)" + ) + + if "archive_doi: null" in manifest or "version_doi: null" in manifest: + human_blockers.append("Zenodo DOI not yet minted (see paper/ZENODO.md) — USER-ACTION") + human_blockers.append("ScholarOne upload — USER-ACTION") + human_blockers.append("Co-author final confirmation — USER-ACTION") + human_blockers.append("APC / Read & Publish / ISCB discount check — USER-ACTION") + + for warning in warnings: + print(f"WARNING: {warning}") + + if errors: + for error in errors: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + + print("submission package machine checks PASSED") + print(f"approximate word/token count: {n_words}") + print("remaining USER-ACTION blockers (not machine failures):") + for blocker in human_blockers: + print(f"- {blocker}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/paper/cover-letter.md b/paper/cover-letter.md new file mode 100644 index 0000000..6fd544d --- /dev/null +++ b/paper/cover-letter.md @@ -0,0 +1,42 @@ +# Cover letter — *Bioinformatics* Application Note (draft) + +**To:** The Editors, *Bioinformatics* (Oxford University Press) +**From:** Ehsan Estaji and Jian-Feng Mao +**Re:** Application Note submission — *FastaGuard: FASTA preflight QC for modern bioinformatics pipelines* + +Dear Editors, + +Please consider our manuscript as an **Application Note** in *Bioinformatics*. + +FastaGuard is an open-source (MIT), streaming-first Rust command-line tool that +provides **FASTA preflight quality control** for genome-assembly pipelines. It +validates FASTA structure, flags identifier and composition hazards, and emits a +stable multi-format report contract (HTML, JSON, TSV, and MultiQC-compatible +JSON) with explicit pipeline gates (`gate.can_continue`) before expensive +downstream steps such as QUAST, BUSCO, BlobToolKit, CheckM, annotation, or +official repository validators. The contribution is a pipeline-native preflight +*contract*—not interpretive assembly QC and not a substitute for those tools—so +workflow authors can stop or route early on FASTA-level blockers. The software +is published on Bioconda and BioContainers (v1.0.0) with merged nf-core module +and Snakemake wrapper integrations. + +We believe *Bioinformatics* Application Notes are the appropriate venue for a +short, citable description of freely available bioinformatics software with clear +Availability and Implementation. The manuscript follows Application Note length +and abstract conventions (Summary / Availability and Implementation / Contact / +Supplementary information). Source and documentation: +https://github.com/ehsanestaji/FastaGuard + +**Suggested topical area:** genome assembly / sequence analysis software tools +(Editors may reassign as appropriate.) + +Both corresponding authors can be reached at ehsan.estaji@umu.se and +jianfeng.mao@umu.se. We confirm that the work is original, is not under +consideration elsewhere, and that all authors approve submission. + +Thank you for your consideration. + +Sincerely, +Ehsan Estaji +Jian-Feng Mao +Umeå Plant Science Centre, Department of Plant Physiology, Umeå University diff --git a/paper/main.tex b/paper/main.tex index 05f76d9..43beb50 100644 --- a/paper/main.tex +++ b/paper/main.tex @@ -65,8 +65,11 @@ \noindent\textbf{Availability and Implementation:} Source code and documentation are freely available under the MIT license at -\url{https://github.com/ehsanestaji/FastaGuard}. -Install via Bioconda (\texttt{mamba install -c conda-forge -c bioconda fastaguard}), +\url{https://github.com/ehsanestaji/FastaGuard} +(release tag \texttt{v1.0.0}: +\url{https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0}). +Install via Bioconda +(\texttt{mamba install -c conda-forge -c bioconda fastaguard=1.0.0}), BioContainers, or GitHub release binaries. Implemented in Rust; supported on Linux and macOS, including Bioconda builds for \texttt{linux-64}, \texttt{linux-aarch64}, \texttt{osx-64}, and \texttt{osx-arm64}. @@ -202,8 +205,9 @@ \section{Implementation} binaries for major Linux and macOS targets, and has merged nf-core module and Snakemake wrapper integrations for assembly preflight. A typical install path is -\texttt{mamba install -c conda-forge -c bioconda fastaguard=1.0.0}, with a -pinned BioContainers image for containerized workflow engines. +\texttt{mamba install -c conda-forge -c bioconda fastaguard=1.0.0} +(matching GitHub release tag \texttt{v1.0.0}), with a pinned BioContainers image +for containerized workflow engines. These distribution paths are part of the Application Note claim: the tool is meant to be installed and gated in real pipelines with low friction, not only built from source by specialists. @@ -256,10 +260,10 @@ \section{Results} SHA-256 digests, structural metrics, finding identifiers, and scoped provenance separately for the observed executable and the verified source tree. On a recorded macOS arm64 run, a clean \emph{Escherichia coli} K-12 MG1655 -assembly (\texttt{GCF\_000005845.2}; 4.64~Mbp) completed pipeline-gated -preflight in 0.06~s with PASS and no findings. -A \emph{Neurospora crassa} OR74A assembly (\texttt{GCF\_000182925.2}; 41.1~Mbp) -completed in 0.34~s with WARN for gap-pattern advisories and +assembly (\texttt{GCF\_000005845.2}; 4\,641\,652~bp) completed pipeline-gated +preflight in 0.0569~s with PASS and no findings. +A \emph{Neurospora crassa} OR74A assembly (\texttt{GCF\_000182925.2}; 41\,102\,378~bp) +completed in 0.3441~s with WARN for gap-pattern advisories and \texttt{gate.can\_continue} still true under the pipeline gate, routing to deeper assembly or composition follow-up rather than hard failure. An intentionally broken local fixture returned FAIL with blocking findings @@ -283,20 +287,19 @@ \section{Results} \begin{table}[t] \centering - \caption{Illustrative FastaGuard v0.6.0 public-evidence outcomes (assembly - profile, pipeline gate). Elapsed times are single-machine contextual - measurements from the committed evidence pack, not cross-platform guarantees. - Full portable JSON/TSV summaries are in the software repository - (\texttt{docs/evidence/}).} + \caption{Frozen FastaGuard v0.6.0 public-evidence outcomes (assembly profile, + pipeline gate) from \texttt{docs/evidence/results/v0.6/evidence\_summary.tsv}. + Elapsed times are single-machine contextual measurements, not cross-platform + guarantees. Software Availability cites the published \texttt{v1.0.0} release.} \label{tab:evidence} \begin{tabular}{@{}llllr@{}} \toprule Input & Scale & Verdict & Gate & Time (s) \\ \midrule - Synthetic valid fixture & 604~bp / 4 seqs & PASS & PASS & 0.019 \\ - Problem fixture & 145~bp / 5 seqs & FAIL & FAIL & 0.019 \\ - \emph{E.\ coli} \texttt{GCF\_000005845.2} & 4.64~Mbp / 1 seq & PASS & PASS & 0.057 \\ - \emph{N.\ crassa} \texttt{GCF\_000182925.2} & 41.1~Mbp / 21 seqs & WARN & WARN & 0.344 \\ + Synthetic valid fixture & 604~bp / 4 seqs & PASS & PASS & 0.0194 \\ + Problem fixture & 145~bp / 5 seqs & FAIL & FAIL & 0.0185 \\ + \emph{E.\ coli} \texttt{GCF\_000005845.2} & 4\,641\,652~bp / 1 seq & PASS & PASS & 0.0569 \\ + \emph{N.\ crassa} \texttt{GCF\_000182925.2} & 41\,102\,378~bp / 21 seqs & WARN & WARN & 0.3441 \\ \bottomrule \end{tabular} \end{table} @@ -368,9 +371,10 @@ \section*{Acknowledgements} \section*{Data availability} FastaGuard source code, schemas, finding catalog, and documentation are openly available at \url{https://github.com/ehsanestaji/FastaGuard} under the MIT -license. +license (cited release tag \texttt{v1.0.0}). Committed public-evidence summaries used in Table~\ref{tab:evidence} are -archived under \texttt{docs/evidence/} in the repository. +archived under \texttt{docs/evidence/} (v0.6.0 portable JSON/TSV pack) in the +repository. No new biological samples were generated for this study; public NCBI assemblies cited in the evidence pack are used under their existing data-access terms. diff --git a/paper/references.bib b/paper/references.bib index 72dce9f..6b16496 100644 --- a/paper/references.bib +++ b/paper/references.bib @@ -64,10 +64,10 @@ @article{shen2016seqkit } @misc{fastaguard2026, - author = {Estaji, Ehsan}, + author = {Estaji, Ehsan and Mao, Jian-Feng}, title = {{FastaGuard}}, year = {2026}, - howpublished = {\url{https://github.com/ehsanestaji/FastaGuard}}, + howpublished = {\url{https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0}}, note = {Version 1.0.0; MIT License} } diff --git a/paper/submission.yml b/paper/submission.yml new file mode 100644 index 0000000..2526879 --- /dev/null +++ b/paper/submission.yml @@ -0,0 +1,109 @@ +# FastaGuard Bioinformatics Application Note — submission control manifest +# Validated by: python3 paper/check_submission.py + +target_journal: Bioinformatics +publisher: Oxford University Press +article_type: Application Note +submission_portal: ScholarOne (Bioinformatics) +format_policy: format-free_first_submission_ok +oup_template_deferred_to: revision + +# Machine-check status after `make check` (human ScholarOne/Zenodo steps remain USER-ACTION). +submission_status: ready_for_author_upload +author_gate_status: pending_coauthor_confirmation +zenodo_gate_status: pending_user_deposit +scholarone_status: not_submitted + +manuscript: + source: paper/main.tex + bibliography: paper/references.bib + pdf: paper/main.pdf + biorxiv_pdf: paper/build/fastaguard-biorxiv.pdf + title: "FastaGuard: FASTA preflight QC for modern bioinformatics pipelines" + cover_letter: paper/cover-letter.md + +authors: + - name: Ehsan Estaji + affiliation: Umeå Plant Science Centre, Department of Plant Physiology, Umeå University + email: ehsan.estaji@umu.se + orcid: 0009-0005-0838-228X + corresponding: true + - name: Jian-Feng Mao + affiliation: Umeå Plant Science Centre, Department of Plant Physiology, Umeå University + email: jianfeng.mao@umu.se + orcid: 0000-0001-9735-8516 + corresponding: true + +# Software version cited for Availability / packaging (published GitHub + Bioconda release). +release: + tag: v1.0.0 + source_archive_url: https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0 + bioconda_package: fastaguard=1.0.0 + archive_doi: null + version_doi: null + zenodo_notes: USER-ACTION — mint DOI from existing GitHub release v1.0.0 (see paper/ZENODO.md); do not invent DOIs. + +# Public-evidence freeze used by Table 1 (contract illustrations; not a cross-platform benchmark). +evidence_freeze: + pack: docs/evidence/fastaguard-v0.6-public-evidence.md + portable_tsv: docs/evidence/results/v0.6/evidence_summary.tsv + portable_json: docs/evidence/results/v0.6/evidence_summary.json + fastaguard_version: "0.6.0" + source_commit: cf27295da0cb9b1a48318caa9e3b8739cfd0c104 + generated_at: "2026-08-21T08:11:21+00:00" + table_rows: + - id: synthetic_valid + elapsed_seconds: "0.0194" + verdict: PASS + gate_status: PASS + total_length: 604 + sequence_count: 4 + - id: problem_fixture + elapsed_seconds: "0.0185" + verdict: FAIL + gate_status: FAIL + total_length: 145 + sequence_count: 5 + - id: ecoli_k12_mg1655 + accession: GCF_000005845.2 + elapsed_seconds: "0.0569" + verdict: PASS + gate_status: PASS + total_length: 4641652 + sequence_count: 1 + - id: neurospora_crassa_or74a + accession: GCF_000182925.2 + elapsed_seconds: "0.3441" + verdict: WARN + gate_status: WARN + total_length: 41102378 + sequence_count: 21 + +claim_policy: + forbidden_phrase: "FastQC for FASTA" + broad_replacement_claim_allowed: false + performance_claim_source: docs/evidence_only + caveats_required: true + +submission_docs: + - docs/paper-readiness.md + - paper/cover-letter.md + - paper/ZENODO.md + - paper/biorxiv-checklist.md + - paper/HANDOFF.md + - paper/README.md + +required_human_inputs: + - coauthor_final_ok_jian_feng_mao + - zenodo_doi_from_v1.0.0_release + - optional_biorxiv_deposit + - scholarone_upload + - apc_or_read_and_publish_check + - suggested_editors_or_accept_defaults + +blockers_for_machine_ready: [] +blockers_for_human_submit: + - ScholarOne upload (credentials) + - Zenodo DOI mint from GitHub release v1.0.0 (credentials / GitHub–Zenodo link) + - Co-author final confirmation + - APC / institutional Read & Publish / ISCB discount check From 6120adc7eb2f0e82566ca69412eff30b2763a5d5 Mon Sep 17 00:00:00 2001 From: Ehsan ESTAJI <71376358+ehsanestaji@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:24:19 +0200 Subject: [PATCH 5/9] Add Zenodo DOI for GitHub release v1.0.0 to Availability. Minted version DOI 10.5281/zenodo.23622311 (concept 10.5281/zenodo.23622310) from the existing v1.0.0 release via Zenodo GitHub integration. --- paper/HANDOFF.md | 6 ++++++ paper/ZENODO.md | 7 +++++++ paper/main.tex | 3 ++- paper/submission.yml | 9 +++++---- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md index 60515e0..9413c86 100644 --- a/paper/HANDOFF.md +++ b/paper/HANDOFF.md @@ -41,3 +41,9 @@ VariantFlow VCF claims, benchmarks, tables, Zenodo/bioRxiv DOIs. - Manifest: `paper/submission.yml` - Readiness: `docs/paper-readiness.md` - Store context: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` + +## Zenodo DOI (2026-10-24) + +- Version DOI: `10.5281/zenodo.23622311` (https://zenodo.org/records/23622311) +- Concept DOI: `10.5281/zenodo.23622310` +- Source: GitHub release `v1.0.0` via Zenodo GitHub integration diff --git a/paper/ZENODO.md b/paper/ZENODO.md index a991053..0be80b5 100644 --- a/paper/ZENODO.md +++ b/paper/ZENODO.md @@ -31,3 +31,10 @@ exists and is the cited Availability / Bioconda line - Invent or placeholder a `10.5281/zenodo.…` DOI in the submit-ready PDF. - Retag a different software version unless the manuscript Availability is updated to match. + +## Minted (2026-10-24) + +- **Version DOI:** `10.5281/zenodo.23622311` — https://doi.org/10.5281/zenodo.23622311 +- **Concept DOI:** `10.5281/zenodo.23622310` +- **Record:** https://zenodo.org/records/23622311 +- Pasted into `paper/main.tex` Availability and `paper/submission.yml`. diff --git a/paper/main.tex b/paper/main.tex index 43beb50..07b682d 100644 --- a/paper/main.tex +++ b/paper/main.tex @@ -67,7 +67,8 @@ Source code and documentation are freely available under the MIT license at \url{https://github.com/ehsanestaji/FastaGuard} (release tag \texttt{v1.0.0}: -\url{https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0}). +\url{https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0}; +archived at \href{https://doi.org/10.5281/zenodo.23622311}{doi:10.5281/zenodo.23622311}). Install via Bioconda (\texttt{mamba install -c conda-forge -c bioconda fastaguard=1.0.0}), BioContainers, or GitHub release binaries. diff --git a/paper/submission.yml b/paper/submission.yml index 2526879..d75dae0 100644 --- a/paper/submission.yml +++ b/paper/submission.yml @@ -11,7 +11,7 @@ oup_template_deferred_to: revision # Machine-check status after `make check` (human ScholarOne/Zenodo steps remain USER-ACTION). submission_status: ready_for_author_upload author_gate_status: pending_coauthor_confirmation -zenodo_gate_status: pending_user_deposit +zenodo_gate_status: deposited scholarone_status: not_submitted manuscript: @@ -39,9 +39,10 @@ release: tag: v1.0.0 source_archive_url: https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0 bioconda_package: fastaguard=1.0.0 - archive_doi: null - version_doi: null - zenodo_notes: USER-ACTION — mint DOI from existing GitHub release v1.0.0 (see paper/ZENODO.md); do not invent DOIs. + archive_doi: "10.5281/zenodo.23622310" + version_doi: "10.5281/zenodo.23622311" + zenodo_record_url: "https://zenodo.org/records/23622311" + zenodo_notes: "Minted 2026-10-24 from GitHub release v1.0.0 via Zenodo GitHub webhook replay; concept DOI 10.5281/zenodo.23622310; version DOI 10.5281/zenodo.23622311." # Public-evidence freeze used by Table 1 (contract illustrations; not a cross-platform benchmark). evidence_freeze: From a679c3b5d862b18be804fab472cc196ed8243a64 Mon Sep 17 00:00:00 2001 From: Ehsan ESTAJI <71376358+ehsanestaji@users.noreply.github.com> Date: Sat, 24 Oct 2026 20:05:21 +0200 Subject: [PATCH 6/9] docs(paper): apply pre-submission review fixes to the Application Note Claims re-tested against a binary built from the v1.0.0 tag; related work and references audited against Crossref/DataCite. - Table 1: state --min-contig-length 1 (default => synthetic fixture WARNs), note N. crassa count includes mitochondrion NC_026614.1, report that v1.0.0 reproduces the two local fixtures - Remove unsupported benchmark sentence, "fast", SNIC/HPC2N acknowledgement, and the paper/ supplementary pointer (not in the v1.0.0 tree) - Correct gate blockers, release-binary platforms, determinism wording and the NCBI policy description - Add related work (py_fasta_validator, fa-lint, AssemblyQC, SeqFu, gfastats) and missing citations; 24 verified bib entries, 0 BibTeX warnings - Fix --flag en-dash ligature, PDF metadata, Fig. 1 label overlap - Cover letter: preprint, AI-use and archive-DOI disclosures (OUP policy) - Handoff docs, manifest and bioRxiv checklist updated Co-Authored-By: Claude Sonnet 5.5 --- docs/paper-readiness.md | 10 +- paper/HANDOFF.md | 50 +++++++++- paper/biorxiv-checklist.md | 57 +++++++---- paper/cover-letter.md | 18 +++- paper/main.tex | 161 ++++++++++++++++++------------ paper/references.bib | 199 ++++++++++++++++++++++++++++++++++--- paper/submission.yml | 14 ++- 7 files changed, 400 insertions(+), 109 deletions(-) diff --git a/docs/paper-readiness.md b/docs/paper-readiness.md index feadac0..241a5f3 100644 --- a/docs/paper-readiness.md +++ b/docs/paper-readiness.md @@ -17,7 +17,11 @@ Machine check: `cd paper && make check`. | Cited software release for Availability: GitHub **`v1.0.0`** (Bioconda-published) | DONE | | Forbidden phrase guard (“FastQC for FASTA”) in `check_submission.py` | DONE | | `.zenodo.json` metadata prepared (no fake DOI) | DONE | -| Cover letter draft (`paper/cover-letter.md`) | DONE | +| Zenodo DOI minted from `v1.0.0` (`10.5281/zenodo.23622311`) and cited in Availability + manifest | DONE | +| Cover letter draft (`paper/cover-letter.md`) incl. preprint + AI disclosure + archive DOI (OUP policy) | DONE | +| Upstream claims verified (Bioconda, BioContainers, nf-core module, Snakemake wrapper) | DONE | +| Pre-submission review: claims re-tested against a v1.0.0 build; related work + references audited; fixes applied (see `paper/HANDOFF.md`) | DONE | +| Zenodo record creators corrected (Estaji + Mao, ORCIDs, affiliation) | DONE | | bioRxiv build target + checklist | DONE | | PDF builds (`make`) | DONE | | Word count ~2.0–2.6k band | DONE (~2.0k+) | @@ -27,8 +31,8 @@ Machine check: `cd paper && make check`. | Item | Notes | | --- | --- | | Co-author final OK (Jian-Feng Mao) | Confirm authorship/order/CRediT | -| Zenodo DOI from existing `v1.0.0` release | Follow [`paper/ZENODO.md`](../paper/ZENODO.md); then paste DOI into Availability + `submission.yml` and rebuild | -| Optional bioRxiv deposit | Follow [`paper/biorxiv-checklist.md`](../paper/biorxiv-checklist.md) | +| Review decisions | AI-disclosure wording, funding line, SNIC ack, optional feature matrix / differential test, optional NCBI re-run at v1.0.0 (see `paper/HANDOFF.md`) | +| bioRxiv deposit | Follow [`paper/biorxiv-checklist.md`](../paper/biorxiv-checklist.md); all authors must consent; cover letter discloses it | | ScholarOne upload | Format-free first submission OK; upload PDF + cover letter; OUP General Template deferred to **revision** | | APC / Read & Publish / ISCB discount | Check before acceptance | | Suggested editors (optional) | Cover letter suggests assembly/sequence-analysis tools area | diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md index 9413c86..71e7820 100644 --- a/paper/HANDOFF.md +++ b/paper/HANDOFF.md @@ -9,9 +9,12 @@ PR: [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · branch `cursor/p | Status | Items | | --- | --- | | **DONE / submit-package ready** | Full draft; front-matter; evidence freeze; `submission.yml`; `check_submission.py`; cover letter; `.zenodo.json`; bioRxiv build target; `docs/paper-readiness.md` | -| **USER-ACTION only** | Co-author final OK; Zenodo DOI mint (`paper/ZENODO.md`); optional bioRxiv; ScholarOne upload; APC/R&P/ISCB | +| **DONE (2026-10-24)** | Zenodo DOI minted and pasted into Availability + manifest; OUP guideline review (AI + preprint disclosure added to cover letter); external claims verified (see below); PDF metadata + Fig. 1 label overlap fixed | +| **USER-ACTION only** | Co-author final OK (Jian-Feng Mao); bioRxiv deposit; ScholarOne upload; APC/R&P/ISCB | -Do **not** claim ScholarOne submitted from this agent track. +Do **not** claim bioRxiv or ScholarOne submitted from this agent track until the +confirmation page / DOI is seen. Never click final Submit without explicit user OK. +Order: co-author OK -> bioRxiv -> ScholarOne (cover letter says the preprint was submitted). ## Commands @@ -42,6 +45,49 @@ VariantFlow VCF claims, benchmarks, tables, Zenodo/bioRxiv DOIs. - Readiness: `docs/paper-readiness.md` - Store context: `/cursor/stores/bc-53e26eb2-1f92-41f8-9fa2-384c56a6ad53/docs/project-context.md` +## Verified 2026-10-24 (do not re-derive) + +- `fastaguard=1.0.0` is Bioconda latest; BioContainers `quay.io/biocontainers/fastaguard:1.0.0--hfa8f182_0` exists. +- nf-core module `modules/nf-core/fastaguard` and `snakemake-wrappers/bio/fastaguard` exist upstream. +- Zenodo DOI resolves (HTTP 200 -> zenodo.org/records/23622311). +- Body ~2,160 words excl. references (OUP limit ~2,600 words, or ~2,000 + one figure). +- GitHub "Latest" is v1.1.0 but the paper intentionally cites v1.0.0 (Bioconda latest). + +## Pre-submission review (2026-10-24) - applied + +Two independent reviews (claims vs a binary built from `git archive v1.0.0`; related work and +references) plus an editorial pass. Applied to `main.tex` / `references.bib`: + +- Table 1 caption now states runs used `--min-contig-length 1` (default => synthetic fixture WARNs on + `tiny_contigs`; verified), the N. crassa count includes mitochondrion NC_026614.1 (64,840 bp; verified + against NCBI Datasets), and the v1.0.0 binary reproduces both local fixtures (verdict, gate, SHA-256, exit 0). +- Removed the unsupported "value-benchmark" sentence (source was v0.3.0 in `docs/value-benchmark.md`, cited to v1.0.0). +- Gate text lists first-token duplicate IDs; binaries claim fixed (no Linux aarch64 GitHub binary; Bioconda has it); + "deterministic bundle" narrowed (timestamps/durations vary); NCBI policy described as conservative (stricter than + table2asn on contig_199 / seqid_50 boundary cases) with the NCBI FASTA-format citation. +- Related work added (py_fasta_validator, fa-lint, AssemblyQC, SeqFu, gfastats, SeqKit2) with hedged "to our + knowledge" wording; missing citations added (CheckM, Nextflow, Snakemake, nf-core, Bioconda, BioContainers, FCS-GX, + RefSeq and genome papers). All DOIs checked against Crossref/DataCite; 24 bib entries, 0 BibTeX warnings. +- Removed: "fast", "product thesis", "future tool-using agents", "falsifiable", SNIC/HPC2N acknowledgement (all + reported runs were macOS arm64), and the `paper/` supplementary pointer (not in the v1.0.0 tree). +- Zenodo record 23622311 creators fixed on zenodo.org (Estaji + Mao, ORCIDs, UPSC affiliation); DOI unchanged. +- PDF: `--flag` ligature fixed (microtype `\DisableLigatures`), PDF metadata filled, Fig. 1 label overlap fixed. + +Open items (user decisions, NOT done): AI-disclosure wording vs OUP rule; funding line confirmation; +restore SNIC ack only if HPC2N was really used; optional feature-matrix + differential test vs +py_fasta_validator/fa-lint/table2asn (referee will likely ask); optional re-run of the two NCBI rows with v1.0.0; +`CITATION.cff` lists Estaji only (product repo); Zenodo description still says Bioconda "remains at v0.7.0". +Length: body ~2,270 words (limit ~2,600) but 24 references push the draft PDF to 7 pages. + +## OUP policy points that shaped the cover letter + +- Preprints allowed; must be disclosed in the cover letter; update with published DOI after acceptance; + do not submit the preprint to more than one journal at once. +- AI use must be disclosed in the cover letter and in the manuscript. OUP lists "drafting papers from a + prompt" as unacceptable; the disclosure must stay an accurate description of what was done. +- Software must be archived (Zenodo) with the URL in the Availability abstract, and stay available 2 years. +- ScholarOne asks for ORCID, COI, CRediT roles; reviewer suggestions optional. + ## Zenodo DOI (2026-10-24) - Version DOI: `10.5281/zenodo.23622311` (https://zenodo.org/records/23622311) diff --git a/paper/biorxiv-checklist.md b/paper/biorxiv-checklist.md index 7db6c2a..99c6e2c 100644 --- a/paper/biorxiv-checklist.md +++ b/paper/biorxiv-checklist.md @@ -1,34 +1,57 @@ -# bioRxiv upload checklist (USER-ACTION, optional) +# bioRxiv upload checklist (USER-ACTION) -Prepare a preprint **before or with** ScholarOne submission. OUP allows -preprints; update the preprint with the published DOI after acceptance. +Deposit the preprint **before** the ScholarOne submission: the cover letter +states that the same manuscript has been submitted to bioRxiv (OUP requires +preprint disclosure in the cover letter). OUP allows preprints; update the +preprint with the published DOI after acceptance. ## Build the bioRxiv-ready PDF ```bash cd paper make biorxiv -# → paper/build/fastaguard-biorxiv.pdf +# -> paper/build/fastaguard-biorxiv.pdf ``` This is the same Application Note PDF with a stable filename for upload. +bioRxiv takes a single PDF with text, figures, and tables; no separate figure +files are needed. + +## Gate before starting + +- Jian-Feng Mao has confirmed in writing that he consents to deposition and + submission (bioRxiv requires all authors' consent). +- Ehsan is logged in to bioRxiv himself (registration needs an affiliation: + Umeå Plant Science Centre, Umeå University). Agents never enter passwords or + create accounts. + +## Fields (paste-ready) + +| Field | Value | +| --- | --- | +| Title | `FastaGuard: FASTA preflight QC for modern bioinformatics pipelines` | +| Authors | Ehsan Estaji (ORCID 0009-0005-0838-228X); Jian-Feng Mao (ORCID 0000-0001-9735-8516) | +| Affiliation | Umeå Plant Science Centre, Department of Plant Physiology, Umeå University, SE-901 87 Umeå, Sweden | +| Corresponding | ehsan.estaji@umu.se, jianfeng.mao@umu.se | +| Article type | New Results | +| Subject area | Bioinformatics (confirm the name in bioRxiv's dropdown) | +| Abstract | Summary paragraph from `main.tex` (single block); Availability stays in the PDF | +| License | CC BY (recommended: matches the open-access CC BY licence OUP offers) | +| Funding | WIFORCE (Wallenberg Initiatives in Forest Research), Knut and Alice Wallenberg Foundation | +| Competing interests | None | +| Code / data | https://github.com/ehsanestaji/FastaGuard ; Zenodo doi:10.5281/zenodo.23622311 | ## Upload steps -1. Create/sign in to a bioRxiv account. -2. Start a new preprint in **Bioinformatics** / computational biology category - (choose the closest bioRxiv subject collection). -3. Upload `paper/build/fastaguard-biorxiv.pdf`. -4. Title: `FastaGuard: FASTA preflight QC for modern bioinformatics pipelines` -5. Authors / affiliations / ORCIDs as in `paper/main.tex`. -6. Abstract: paste the structured Summary paragraph (bioRxiv may want a single - abstract block — use the Summary text; keep Availability as a separate - statement in the PDF). -7. Confirm license compatible with later OUP CC BY publication. -8. After posting, record the bioRxiv DOI in `paper/submission.yml` notes and - optionally in Acknowledgements / Data availability. +1. Start a new submission, upload `paper/build/fastaguard-biorxiv.pdf`. +2. Fill the fields above; review the PDF as bioRxiv renders it. +3. Stop at the final confirm screen and get Ehsan's explicit OK before clicking + it: a posted preprint is citable and cannot be removed. +4. After bioRxiv screening, record the bioRxiv DOI in `paper/submission.yml` + (`biorxiv_status`) and add it to the ScholarOne record / revision. ## Do not -- Claim the preprint is posted until the bioRxiv confirmation page shows a DOI. +- Claim the preprint is posted until the bioRxiv confirmation shows a DOI. - Put a fake bioRxiv DOI in `main.tex`. +- Submit the preprint to more than one journal at once (OUP policy). diff --git a/paper/cover-letter.md b/paper/cover-letter.md index 6fd544d..ce93424 100644 --- a/paper/cover-letter.md +++ b/paper/cover-letter.md @@ -11,7 +11,7 @@ Please consider our manuscript as an **Application Note** in *Bioinformatics*. FastaGuard is an open-source (MIT), streaming-first Rust command-line tool that provides **FASTA preflight quality control** for genome-assembly pipelines. It validates FASTA structure, flags identifier and composition hazards, and emits a -stable multi-format report contract (HTML, JSON, TSV, and MultiQC-compatible +versioned multi-format report contract (HTML, JSON, TSV, and MultiQC-compatible JSON) with explicit pipeline gates (`gate.can_continue`) before expensive downstream steps such as QUAST, BUSCO, BlobToolKit, CheckM, annotation, or official repository validators. The contribution is a pipeline-native preflight @@ -30,9 +30,23 @@ https://github.com/ehsanestaji/FastaGuard **Suggested topical area:** genome assembly / sequence analysis software tools (Editors may reassign as appropriate.) +**Software availability.** The submitted release (v1.0.0) is open source (MIT), +available at the stable URL above, and archived on Zenodo +(doi:10.5281/zenodo.23622311) as required by the journal software policy. + +**Preprint disclosure.** The same manuscript has been submitted to bioRxiv as a +preprint. We will update the preprint with the published DOI after acceptance. + +**AI use disclosure.** AI coding assistants (including Cursor-based agents and +related large-language-model tools) were used for planning, manuscript +scaffolding, repository evidence cross-checks, and prose drafting support. The +authors reviewed all output and remain responsible for the manuscript, code, +evidence interpretation, and all scientific claims. The same statement appears in +the manuscript. + Both corresponding authors can be reached at ehsan.estaji@umu.se and jianfeng.mao@umu.se. We confirm that the work is original, is not under -consideration elsewhere, and that all authors approve submission. +consideration at another journal, and that all authors approve submission. Thank you for your consideration. diff --git a/paper/main.tex b/paper/main.tex index 07b682d..4db8109 100644 --- a/paper/main.tex +++ b/paper/main.tex @@ -21,7 +21,7 @@ \usepackage{xcolor} \usepackage{natbib} \usepackage{url} -\usepackage{microtype} +\usepackage{microtype}\DisableLigatures[-]{encoding=*,family=tt*}% keep "--" in CLI flags as two hyphens \usepackage{lineno} \usepackage{amssymb} \usepackage{tikz} @@ -33,7 +33,10 @@ colorlinks=true, linkcolor=fgblue, citecolor=fgblue, - urlcolor=fgblue + urlcolor=fgblue, + pdftitle={FastaGuard: FASTA preflight QC for modern bioinformatics pipelines}, + pdfauthor={Ehsan Estaji and Jian-Feng Mao}, + pdfkeywords={FASTA, quality control, genome assembly, preflight, bioinformatics pipelines, Rust} } \setlength{\emergencystretch}{2em} @@ -58,9 +61,10 @@ \begin{abstract} \noindent\textbf{Summary:} -FastaGuard is a fast, explainable FASTA preflight QC tool that validates assembly -FASTA files, detects structural and composition red flags, and emits a stable -multi-format report contract before expensive downstream analysis such as QUAST, +FastaGuard is a streaming, explainable FASTA preflight QC tool that validates assembly +FASTA files, detects structural and composition red flags, and emits a versioned +multi-format report with an explicit pipeline gate before expensive downstream +analysis such as QUAST, BUSCO, BlobToolKit, CheckM, annotation, or official repository validation. \noindent\textbf{Availability and Implementation:} @@ -81,7 +85,7 @@ \noindent\textbf{Supplementary information:} Finding-catalog excerpts, evidence packs, and reproducibility notes are maintained -with the software repository under \texttt{docs/} and \texttt{paper/}. +with the software repository under \texttt{docs/}. \end{abstract} \vspace{0.4em} @@ -90,31 +94,40 @@ \section{Introduction} Genome assemblies and reference FASTA files are foundational inputs for -bioinformatics pipelines, yet FASTA-level quality control remains fragmented. +bioinformatics pipelines, yet FASTA-level checks are usually assembled from +separate utilities. Teams commonly combine ad hoc scripts, general sequence utilities such as -SeqKit~\citep{shen2016seqkit}, assembly evaluators such as +SeqKit~\citep{shen2016seqkit,shen2024seqkit2}, assembly evaluators such as QUAST~\citep{gurevich2013quast}, biological completeness tools such as -BUSCO~\citep{simao2015busco}, contamination-oriented exploration with +BUSCO~\citep{simao2015busco}, genome-quality estimation with +CheckM~\citep{parks2015checkm}, contamination-oriented exploration with BlobToolKit~\citep{challis2020blobtoolkit}, and late-stage report aggregation with MultiQC~\citep{ewels2016multiqc}. Raw-read QC tools remain essential before assembly, but they do not answer whether a finished assembly FASTA is structurally sound for the next stage. -Each of the tools above is valuable for its intended question. -None is the simple default first command for a narrower but frequent need: +Each of the tools above is valuable for its intended question; none is designed +as a first-step gate for a narrower but frequent need: whether a FASTA file is structurally valid, alphabet-sane, interpretable, and safe to pass into expensive downstream steps. The cost of skipping that earlier layer is practical. Malformed headers, empty records, duplicate identifiers, invalid IUPAC -characters, extreme ambiguity, and high-N scaffolds often surface only after -minutes to hours of assembly QC, completeness assessment, taxonomy workflows, -annotation, or official repository validation have already started. -Pipeline authors then reinvent the same checks repeatedly, with incompatible -report shapes and unclear stop/go semantics for workflow engines. -SeqKit and related utilities can compute counts and lengths quickly, but they -are not opinionated QC gates with a versioned schema, MultiQC-compatible -artifacts, and explicit blocking-versus-advisory policy for Nextflow or -Snakemake. +characters, extreme ambiguity, and high-N scaffolds may be caught only when a +downstream tool reaches the offending record, or be silently normalised along +the way; we did not quantify the resulting delay. +Pipeline authors then reimplement the same checks, with incompatible report +shapes and unclear stop/go semantics for workflow engines such as +Nextflow~\citep{ditommaso2017nextflow} and Snakemake~\citep{molder2021snakemake}. +Dedicated validators exist: py\_fasta\_validator~\citep{edwards2023pyfastavalidator} +and fa-lint~\citep{falint} check headers, alphabet, and identifier uniqueness +but, to our knowledge, provide no structured per-finding report, and AssemblyQC +embeds such input checks inside a single pipeline~\citep{rashid2024assemblyqc}. +Statistics toolkits such as SeqKit, SeqFu~\citep{telatin2021seqfu}, and +gfastats~\citep{formenti2022gfastats} mainly summarise sequences and define no +gate semantics. +To our knowledge, none combines severity-graded findings, an explicit +blocking-versus-advisory gate, and a schema-versioned machine-readable report +(JSON, TSV, MultiQC) that workflow engines can branch on. FastaGuard addresses that gap as a dedicated \emph{preflight} layer (Fig.~\ref{fig:overview}). @@ -122,12 +135,14 @@ \section{Introduction} it does not claim biological completeness, contamination taxonomy, or repository acceptance, and it does not replace official NCBI, ENA, or DDBJ validators. Instead it answers whether the FASTA itself is ready for those tools, and it -returns a deterministic, versioned PASS/WARN/FAIL contract that humans, -Nextflow/Snakemake/nf-core workflows, and future tool-using agents can consume +returns a versioned PASS/WARN/FAIL contract, with verdicts, gates, and findings +that are deterministic for a given input and options, that humans and +Nextflow, Snakemake, or nf-core~\citep{ewels2020nfcore} workflows can consume without scraping logs or HTML. -The product thesis is therefore a contract as much as a report: -stable field names, documented finding identifiers, provenance checksums, and -gate continuation booleans that pipelines can depend on across releases. +The design is therefore a contract as much as a report: +field names, documented finding identifiers, provenance checksums, and gate +continuation booleans are intended to remain stable within a schema major +version. \section{Implementation} FastaGuard is a streaming-first command-line tool implemented in Rust and @@ -140,9 +155,11 @@ \section{Implementation} \end{verbatim} \noindent -which writes a deterministic four-report bundle +which writes a four-report bundle (\texttt{.html}, \texttt{.json}, \texttt{.tsv}, and MultiQC-compatible \texttt{\_mqc.json}) under a chosen prefix. +Verdicts, gates, and findings are deterministic for a given input and options; +provenance timestamps and durations vary between runs. JSON is the source of truth for machine routing. A committed JSON Schema and finding catalog document field names, finding identifiers, severity semantics, and suggested actions @@ -161,9 +178,9 @@ \section{Implementation} \paragraph{Pipeline gates and process exit contract.} Optional gates make stop/go policy explicit. -The recommended workflow preset \texttt{--gate pipeline} marks duplicate IDs, -invalid characters, invalid FASTA structure, and high-N content as blocking by -default, while GC and length outliers remain advisory routing signals unless +The recommended workflow preset \texttt{--gate pipeline} marks duplicate identifiers +(full-header or first-token), invalid characters, invalid FASTA structure, and +high-N content as blocking by default, while GC and length outliers remain advisory routing signals unless elevated with \texttt{--fail-on}. JSON exposes \texttt{gate.status}, \texttt{gate.blocking\_findings}, \texttt{gate.advisory\_findings}, \texttt{gate.can\_continue}, and @@ -180,11 +197,14 @@ \section{Implementation} \paragraph{Submission-readiness, compare mode, and reference checks.} \texttt{--gate submission} with \texttt{--submission-target generic|ncbi} performs FASTA-level submission preflight before official validators. -For NCBI genome targeting, FastaGuard applies a documented -\texttt{ncbi\_genome} policy snapshot (first-token SeqID syntax and length -limits, minimum record length, terminal Ns) derived from the NCBI Genome -Submission Guide~\citep{ncbi_genome_submit}; it does not replace NCBI FCS, -annotation validation, or repository acceptance decisions. +For NCBI genome targeting, FastaGuard applies a documented, deliberately +conservative \texttt{ncbi\_genome} policy snapshot (first-token SeqID syntax and +length limits, minimum record length, terminal Ns) based on NCBI submission and +FASTA guidance~\citep{ncbi_genome_submit,ncbi_fasta_format}; in a local +differential check it was stricter than table2asn on several boundary cases +(for example, a 199-bp record or a 50-character SeqID), and it does not replace +NCBI FCS~\citep{astashyn2024fcsgx}, annotation validation, or repository +acceptance decisions. \texttt{fastaguard compare} triages many FASTA files into cohort HTML/JSON/TSV outputs for starter cohort routing before interpretive QC. A separate \texttt{reference} mode checks whether explicitly supplied companion @@ -202,19 +222,20 @@ \section{Implementation} and for MultiQC custom content. \paragraph{Packaging and workflow adoption.} -FastaGuard is published on Bioconda and BioContainers, ships GitHub release -binaries for major Linux and macOS targets, and has merged nf-core module and -Snakemake wrapper integrations for assembly preflight. +FastaGuard is published on Bioconda~\citep{gruning2018bioconda} and +BioContainers~\citep{daveigaleprevost2017biocontainers}, ships GitHub release +binaries for x86\_64 Linux and for Intel and Apple-silicon macOS (Linux aarch64 +is available through Bioconda), and has merged nf-core and Snakemake wrapper +integrations for assembly preflight. A typical install path is \texttt{mamba install -c conda-forge -c bioconda fastaguard=1.0.0} (matching GitHub release tag \texttt{v1.0.0}), with a pinned BioContainers image for containerized workflow engines. These distribution paths are part of the Application Note claim: the tool is -meant to be installed and gated in real pipelines with low friction, not only -built from source by specialists. -Availability commitments follow the journal software policy: source and -published packages remain openly reachable from the project repository under -the MIT license~\citep{fastaguard2026}. +meant to be installed and gated in pipelines with low friction, not only built +from source by specialists; we make no claim about third-party adoption. +The archived release is citable~\citep{fastaguard2026}, and source and +published packages remain openly reachable under the MIT license. \begin{figure}[t] \centering @@ -243,7 +264,7 @@ \section{Implementation} \draw[arr] (rep.south east) -- (bl.north); \draw[arr] (rep) -- (sub); \node[lab, above=0.05cm of fg] {PASS / WARN / FAIL; \texttt{gate.can\_continue}}; - \node[lab, below=1.55cm of fg] + \node[lab, below=2.0cm of fg] {Fix FASTA-level blockers first; interpretive QC remains downstream}; \end{tikzpicture} \caption{FastaGuard as the FASTA preflight layer before interpretive assembly @@ -255,26 +276,25 @@ \section{Implementation} \section{Results} Committed public-evidence summaries for FastaGuard v0.6.0 illustrate the intended -behaviour on synthetic contract fixtures and two exact NCBI reference assemblies -(Table~\ref{tab:evidence}). +behaviour on synthetic contract fixtures and two exact NCBI RefSeq +assemblies~\citep{oleary2016refseq} (Table~\ref{tab:evidence}). Portable JSON and TSV summaries in the repository record input byte sizes, SHA-256 digests, structural metrics, finding identifiers, and scoped provenance separately for the observed executable and the verified source tree. -On a recorded macOS arm64 run, a clean \emph{Escherichia coli} K-12 MG1655 -assembly (\texttt{GCF\_000005845.2}; 4\,641\,652~bp) completed pipeline-gated +On a recorded macOS arm64 run, a clean \emph{Escherichia coli} K-12 +MG1655~\citep{blattner1997ecoli} assembly (\texttt{GCF\_000005845.2}; 4\,641\,652~bp) completed pipeline-gated preflight in 0.0569~s with PASS and no findings. -A \emph{Neurospora crassa} OR74A assembly (\texttt{GCF\_000182925.2}; 41\,102\,378~bp) +A \emph{Neurospora crassa} OR74A~\citep{galagan2003neurospora} assembly (\texttt{GCF\_000182925.2}; 41\,102\,378~bp) completed in 0.3441~s with WARN for gap-pattern advisories and \texttt{gate.can\_continue} still true under the pipeline gate, routing to deeper assembly or composition follow-up rather than hard failure. An intentionally broken local fixture returned FAIL with blocking findings -(duplicate IDs, invalid characters, high-N content) while still exiting~0 after +(duplicate identifiers, invalid characters, high-N content) while still exiting~0 after writing a complete FAIL report---matching the v0.6 workflow contract that separates QC status from process success. -Earlier local value-benchmark runs on synthetic 10--100~Mbp FASTA inputs -likewise completed in under a second with modest RSS~\citep{fastaguard2026}, -supporting the claim that preflight overhead is small relative to typical -downstream QC. +Re-running the two local fixtures with the v1.0.0 binary reproduced the same +verdicts, gate statuses, input SHA-256 digests, and exit status; the two NCBI +assemblies were not re-run. These examples are contract demonstrations, not a representative biological benchmark across taxa or assemblers. @@ -289,9 +309,15 @@ \section{Results} \begin{table}[t] \centering \caption{Frozen FastaGuard v0.6.0 public-evidence outcomes (assembly profile, - pipeline gate) from \texttt{docs/evidence/results/v0.6/evidence\_summary.tsv}. - Elapsed times are single-machine contextual measurements, not cross-platform - guarantees. Software Availability cites the published \texttt{v1.0.0} release.} + pipeline gate) from + \texttt{docs/evidence/results/v0.6/evidence\_summary.tsv}. + All runs used \texttt{--min-contig-length 1}; with the default minimum, the + 604-bp synthetic fixture returns WARN through an advisory tiny-contig finding. + Elapsed times are single-machine contextual measurements (macOS arm64), not + cross-platform guarantees. + The \emph{N.\ crassa} count includes the mitochondrial sequence NC\_026614.1 + (64\,840~bp). Software Availability cites the published \texttt{v1.0.0} + release.} \label{tab:evidence} \begin{tabular}{@{}llllr@{}} \toprule @@ -325,24 +351,27 @@ \section{Discussion} streaming validation, explainable findings, stable schemas, explicit gates, and packaging paths that workflow authors can adopt without reinventing per-pipeline scripts. -Relative to SeqKit, FastaGuard trades general sequence manipulation for an -opinionated QC verdict and gate policy. +Relative to SeqKit and SeqFu, FastaGuard trades general sequence manipulation +for an opinionated QC verdict and gate policy; relative to syntax validators +such as py\_fasta\_validator and fa-lint, it adds severity-graded, explainable +findings and a structured report. Relative to QUAST, BUSCO, BlobToolKit, and CheckM, it runs earlier and answers a different question: whether the FASTA is safe to spend those tools on. Relative to MultiQC, it is a producer of aggregatable content rather than an aggregator. Positioning the tool as preflight---rather than as a substitute for those -ecosystem components---keeps claims falsifiable and complementary. +ecosystem components---keeps it complementary to them. Limitations follow from that scope. FastaGuard does not discover biological contamination, does not certify archive acceptance, and does not replace official validators or FCS-style screens. Public-evidence timings are contextual to one machine and release line; authors should re-measure on their targets before citing absolute performance. -The present Application Note freezes the v1.x reference-contract story suitable -for citation; feature expansion beyond FASTA preflight belongs in software -releases and, if warranted later, a separate research article rather than an -overloaded note. +Finding thresholds are heuristics: we did not measure detection or +false-positive rates, nor compare FastaGuard with other validators on a common +defect corpus. +The behaviour described here corresponds to release v1.0.0; later changes are +recorded in the project changelog. \section*{Author contributions} Following CRediT, E.E.: conceptualisation, software, validation, investigation, @@ -365,7 +394,6 @@ \section*{AI usage disclosure} evidence interpretation, and all scientific claims. \section*{Acknowledgements} -Computing resources provided by SNIC/HPC2N at Ume{\aa} University. We thank packaging and workflow maintainers who exercised Bioconda, BioContainers, nf-core module, and Snakemake wrapper integrations for FastaGuard. @@ -375,7 +403,10 @@ \section*{Data availability} license (cited release tag \texttt{v1.0.0}). Committed public-evidence summaries used in Table~\ref{tab:evidence} are archived under \texttt{docs/evidence/} (v0.6.0 portable JSON/TSV pack) in the -repository. +repository; the rows can be regenerated with +\texttt{scripts/collect\_evidence.py}, which retrieves the NCBI assemblies by +accession with the NCBI Datasets command-line tool and records input SHA-256 +digests in the TSV. No new biological samples were generated for this study; public NCBI assemblies cited in the evidence pack are used under their existing data-access terms. diff --git a/paper/references.bib b/paper/references.bib index 6b16496..1df79e4 100644 --- a/paper/references.bib +++ b/paper/references.bib @@ -1,5 +1,5 @@ -% Core citations for FastaGuard Application Note framing. -% Expand / verify DOIs and pages before submission. +% References for the FastaGuard Application Note. +% Journal-article DOIs checked against Crossref/DataCite on 2026-10-24. @article{gurevich2013quast, author = {Gurevich, Alexey and Saveliev, Vladislav and Vyahhi, Nikolay and Tesler, Glenn}, @@ -45,13 +45,6 @@ @article{ewels2016multiqc doi = {10.1093/bioinformatics/btw354} } -@article{andrews2010fastqc, - author = {Andrews, Simon}, - title = {{FastQC}: a quality control tool for high throughput sequence data}, - year = {2010}, - note = {Available at \url{https://www.bioinformatics.babraham.ac.uk/projects/fastqc/}} -} - @article{shen2016seqkit, author = {Shen, Wei and Le, Shuai and Li, Yan and Hu, Fuquan}, title = {{SeqKit}: a cross-platform and ultrafast toolkit for {FASTA}/{Q} file manipulation}, @@ -63,18 +56,194 @@ @article{shen2016seqkit doi = {10.1371/journal.pone.0163962} } +@article{parks2015checkm, + author = {Parks, Donovan H. and Imelfort, Michael and Skennerton, Connor T. and Hugenholtz, Philip and Tyson, Gene W.}, + title = {{CheckM}: assessing the quality of microbial genomes recovered from isolates, single cells, and metagenomes}, + journal = {Genome Research}, + year = {2015}, + volume = {25}, + number = {7}, + pages = {1043--1055}, + doi = {10.1101/gr.186072.114} +} + +@article{ditommaso2017nextflow, + author = {Di Tommaso, Paolo and Chatzou, Maria and Floden, Evan W. and Barja, Pablo Prieto and Palumbo, Emilio and Notredame, Cedric}, + title = {Nextflow enables reproducible computational workflows}, + journal = {Nature Biotechnology}, + year = {2017}, + volume = {35}, + number = {4}, + pages = {316--319}, + doi = {10.1038/nbt.3820} +} + +@article{molder2021snakemake, + author = {M{\"o}lder, Felix and Jablonski, Kim Philipp and Letcher, Brice and Hall, Michael B. and Tomkins-Tinch, Christopher H. and Sochat, Vanessa and Forster, Jan and Lee, Soohyun and Twardziok, Sven O. and Kanitz, Alexander and Wilm, Andreas and Holtgrewe, Manuel and Rahmann, Sven and Nahnsen, Sven and K{\"o}ster, Johannes}, + title = {Sustainable data analysis with {Snakemake}}, + journal = {F1000Research}, + year = {2021}, + volume = {10}, + pages = {33}, + doi = {10.12688/f1000research.29032.2} +} + +@article{ewels2020nfcore, + author = {Ewels, Philip A. and Peltzer, Alexander and Fillinger, Sven and Patel, Harshil and Alneberg, Johannes and Wilm, Andreas and Garcia, Maxime Ulysse and Di Tommaso, Paolo and Nahnsen, Sven}, + title = {The {nf-core} framework for community-curated bioinformatics pipelines}, + journal = {Nature Biotechnology}, + year = {2020}, + volume = {38}, + number = {3}, + pages = {276--278}, + doi = {10.1038/s41587-020-0439-x} +} + +@article{gruning2018bioconda, + author = {Gr{\"u}ning, Bj{\"o}rn and Dale, Ryan and Sj{\"o}din, Andreas and Chapman, Brad A. and Rowe, Jillian and Tomkins-Tinch, Christopher H. and Valieris, Renan and K{\"o}ster, Johannes and {The Bioconda Team}}, + title = {{Bioconda}: sustainable and comprehensive software distribution for the life sciences}, + journal = {Nature Methods}, + year = {2018}, + volume = {15}, + number = {7}, + pages = {475--476}, + doi = {10.1038/s41592-018-0046-7} +} + +@article{daveigaleprevost2017biocontainers, + author = {da Veiga Leprevost, Felipe and Gr{\"u}ning, Bj{\"o}rn A. and Alves Aflitos, Saulo and R{\"o}st, Hannes L. and Uszkoreit, Julian and Barsnes, Harald and Vaudel, Marc and Moreno, Pablo and Gatto, Laurent and Weber, Jonas and Bai, Mingze and Jimenez, Rafael C. and Sachsenberg, Timo and Pfeuffer, Julianus and Vera Alvarez, Roberto and Griss, Johannes and Nesvizhskii, Alexey I. and Perez-Riverol, Yasset}, + title = {{BioContainers}: an open-source and community-driven framework for software standardization}, + journal = {Bioinformatics}, + year = {2017}, + volume = {33}, + number = {16}, + pages = {2580--2582}, + doi = {10.1093/bioinformatics/btx192} +} + +@article{astashyn2024fcsgx, + author = {Astashyn, Alexander and Tvedte, Eric S. and Sweeney, Deacon and Sapojnikov, Victor and Bouk, Nathan and Joukov, Victor and Mozes, Eyal and Strope, Pooja K. and Sylla, Pape M. and Wagner, Lukas and Bidwell, Shelby L. and Brown, Larissa C. and Clark, Karen and Davis, Emily W. and Smith-White, Brian and Hlavina, Wratko and Pruitt, Kim D. and Schneider, Valerie A. and Murphy, Terence D.}, + title = {Rapid and sensitive detection of genome contamination at scale with {FCS-GX}}, + journal = {Genome Biology}, + year = {2024}, + volume = {25}, + pages = {60}, + doi = {10.1186/s13059-024-03198-7} +} + +@article{rashid2024assemblyqc, + author = {Rashid, Usman and Wu, Chen and Shiller, Jason and Smith, Ken and Crowhurst, Ross and Davy, Marcus and Chen, Ting-Hsuan and Carvajal, Ignacio and Bailey, Sarah and Thomson, Susan and Deng, Cecilia H.}, + title = {{AssemblyQC}: a {Nextflow} pipeline for reproducible reporting of assembly quality}, + journal = {Bioinformatics}, + year = {2024}, + volume = {40}, + number = {8}, + pages = {btae477}, + doi = {10.1093/bioinformatics/btae477} +} + +@article{formenti2022gfastats, + author = {Formenti, Giulio and Abueg, Linelle and Brajuka, Angelo and Brajuka, Nadolina and Gallardo-Alba, Crist{\'o}bal and Giani, Alice and Fedrigo, Olivier and Jarvis, Erich D.}, + title = {Gfastats: conversion, evaluation and manipulation of genome sequences using assembly graphs}, + journal = {Bioinformatics}, + year = {2022}, + volume = {38}, + number = {17}, + pages = {4214--4216}, + doi = {10.1093/bioinformatics/btac460} +} + +@article{shen2024seqkit2, + author = {Shen, Wei and Sipos, Botond and Zhao, Liuyang}, + title = {{SeqKit2}: a {Swiss} army knife for sequence and alignment processing}, + journal = {iMeta}, + year = {2024}, + volume = {3}, + number = {3}, + pages = {e191}, + doi = {10.1002/imt2.191} +} + +@article{telatin2021seqfu, + author = {Telatin, Andrea and Fariselli, Piero and Birolo, Giovanni}, + title = {{SeqFu}: a suite of utilities for the robust and reproducible manipulation of sequence files}, + journal = {Bioengineering}, + year = {2021}, + volume = {8}, + number = {5}, + pages = {59}, + doi = {10.3390/bioengineering8050059} +} + +@misc{edwards2023pyfastavalidator, + author = {Edwards, Rob}, + title = {py\_fasta\_validator: a {Python} extension of the {fasta\_validate} {FASTA} validator}, + year = {2023}, + howpublished = {\url{https://github.com/linsalrob/py_fasta_validator}}, + note = {Version 0.6; MIT License; doi:10.5281/zenodo.5002710} +} + @misc{fastaguard2026, author = {Estaji, Ehsan and Mao, Jian-Feng}, - title = {{FastaGuard}}, + title = {{FastaGuard}: {FASTA} preflight {QC} for assembly pipelines}, year = {2026}, - howpublished = {\url{https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0}}, - note = {Version 1.0.0; MIT License} + publisher = {Zenodo}, + howpublished = {\url{https://doi.org/10.5281/zenodo.23622311}}, + note = {Version 1.0.0; MIT License; source: \url{https://github.com/ehsanestaji/FastaGuard/releases/tag/v1.0.0}} } @misc{ncbi_genome_submit, author = {{National Center for Biotechnology Information}}, - title = {{NCBI} Genome Submission Guide}, - year = {2026}, + title = {Prokaryotic and Eukaryotic Genomes Submission Guide}, + year = {2025}, howpublished = {\url{https://www.ncbi.nlm.nih.gov/genbank/genomesubmit/}}, - note = {Accessed for FastaGuard ncbi\_genome policy snapshot} + note = {Page last updated 2025-07-21; accessed 2026-10-24} +} + +@article{oleary2016refseq, + author = {O'Leary, Nuala A. and Wright, Mathew W. and Brister, J. Rodney and Ciufo, Stacy and Haddad, Diana and McVeigh, Rich and Rajput, Bhanu and Robbertse, Barbara and Smith-White, Brian and Ako-Adjei, Danso and Astashyn, Alexander and Badretdin, Azat and Bao, Yiming and Blinkova, Olga and Brover, Vyacheslav and Chetvernin, Vyacheslav and Choi, Jinna and Cox, Eric and Ermolaeva, Olga and Farrell, Catherine M. and Goldfarb, Tamara and Gupta, Tripti and Haft, Daniel and Hatcher, Eneida and Hlavina, Wratko and Joardar, Vinita S. and Kodali, Vamsi K. and Li, Wenjun and Maglott, Donna and Masterson, Patrick and McGarvey, Kelly M. and Murphy, Michael R. and O'Neill, Kathleen and Pujar, Shashikant and Rangwala, Sanjida H. and Rausch, Daniel and Riddick, Lillian D. and Schoch, Conrad and Shkeda, Andrei and Storz, Susan S. and Sun, Hanzhen and Thibaud-Nissen, Francoise and Tolstoy, Igor and Tully, Raymond E. and Vatsan, Anjana R. and Wallin, Craig and Webb, David and Wu, Wendy and Landrum, Melissa J. and Kimchi, Avi and Tatusova, Tatiana and DiCuccio, Michael and Kitts, Paul and Murphy, Terence D. and Pruitt, Kim D.}, + title = {Reference sequence ({RefSeq}) database at {NCBI}: current status, taxonomic expansion, and functional annotation}, + journal = {Nucleic Acids Research}, + year = {2016}, + volume = {44}, + number = {D1}, + pages = {D733--D745}, + doi = {10.1093/nar/gkv1189} +} + +@article{blattner1997ecoli, + author = {Blattner, Frederick R. and Plunkett, Guy and Bloch, Craig A. and Perna, Nicole T. and Burland, Valerie and Riley, Monica and Collado-Vides, Julio and Glasner, Jeremy D. and Rode, Christopher K. and Mayhew, George F. and Gregor, Jason and Davis, Nelson Wayne and Kirkpatrick, Heather A. and Goeden, Michael A. and Rose, Debra J. and Mau, Bob and Shao, Ying}, + title = {The complete genome sequence of {\textit{Escherichia coli}} {K-12}}, + journal = {Science}, + year = {1997}, + volume = {277}, + number = {5331}, + pages = {1453--1462}, + doi = {10.1126/science.277.5331.1453} +} + +@misc{falint, + author = {{GallVp}}, + title = {fa-lint: a {FASTA} linter and validator}, + year = {2025}, + howpublished = {\url{https://github.com/GallVp/fa-lint}}, + note = {Version 1.2.0; MIT License; accessed 2026-10-24} +} + +@misc{ncbi_fasta_format, + author = {{National Center for Biotechnology Information}}, + title = {{FASTA} Format for Nucleotide Sequences}, + year = {2026}, + howpublished = {\url{https://www.ncbi.nlm.nih.gov/genbank/fastaformat/}}, + note = {Accessed 2026-10-24} +} + +@article{galagan2003neurospora, + author = {Galagan, James E. and Calvo, Sarah E. and Borkovich, Katherine A. and Selker, Eric U. and others}, + title = {The genome sequence of the filamentous fungus {\textit{Neurospora crassa}}}, + journal = {Nature}, + year = {2003}, + volume = {422}, + pages = {859--868}, + doi = {10.1038/nature01554} } diff --git a/paper/submission.yml b/paper/submission.yml index d75dae0..f70a379 100644 --- a/paper/submission.yml +++ b/paper/submission.yml @@ -8,11 +8,16 @@ submission_portal: ScholarOne (Bioinformatics) format_policy: format-free_first_submission_ok oup_template_deferred_to: revision -# Machine-check status after `make check` (human ScholarOne/Zenodo steps remain USER-ACTION). +# Machine-check status after `make check` (human ScholarOne/bioRxiv steps remain USER-ACTION). submission_status: ready_for_author_upload author_gate_status: pending_coauthor_confirmation zenodo_gate_status: deposited +biorxiv_status: not_submitted scholarone_status: not_submitted +# OUP policy: AI use must be disclosed in the cover letter AND the manuscript; preprints must be +# disclosed in the cover letter; update the preprint with the published DOI after acceptance. +ai_disclosure_locations: [manuscript, cover_letter] +preprint_disclosure_location: cover_letter manuscript: source: paper/main.tex @@ -96,15 +101,14 @@ submission_docs: required_human_inputs: - coauthor_final_ok_jian_feng_mao - - zenodo_doi_from_v1.0.0_release - - optional_biorxiv_deposit + - biorxiv_deposit - scholarone_upload - apc_or_read_and_publish_check - suggested_editors_or_accept_defaults blockers_for_machine_ready: [] blockers_for_human_submit: + - Co-author final confirmation (also required by bioRxiv: all authors must consent to deposit) + - bioRxiv deposit (login by Ehsan; affiliation required) - ScholarOne upload (credentials) - - Zenodo DOI mint from GitHub release v1.0.0 (credentials / GitHub–Zenodo link) - - Co-author final confirmation - APC / institutional Read & Publish / ISCB discount check From 8b67822786e439fb90e5d4843d8e08761acd5fbf Mon Sep 17 00:00:00 2001 From: Ehsan ESTAJI <71376358+ehsanestaji@users.noreply.github.com> Date: Sat, 24 Oct 2026 20:06:26 +0200 Subject: [PATCH 7/9] docs(paper): remove trailing whitespace flagged by the CI quality gate The Rust quality gate runs `git grep -nI -E '[[:blank:]]$'` over the repo and has failed on this branch since the cover letter was added. Replace Markdown two-space hard breaks with blank-line-separated lines in paper/cover-letter.md, paper/README.md and docs/paper-readiness.md. No wording changes. Co-Authored-By: Claude Sonnet 5.5 --- docs/paper-readiness.md | 9 ++++++--- paper/README.md | 3 ++- paper/cover-letter.md | 17 +++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/paper-readiness.md b/docs/paper-readiness.md index 241a5f3..e03cb25 100644 --- a/docs/paper-readiness.md +++ b/docs/paper-readiness.md @@ -1,8 +1,11 @@ # FastaGuard paper readiness (*Bioinformatics* Application Note) -Venue: **Bioinformatics (OUP) — Application Note** (user-confirmed). -Branch/PR: `cursor/paper-prep-venues-198e` / [#21](https://github.com/ehsanestaji/FastaGuard/pull/21). -Control manifest: [`paper/submission.yml`](../paper/submission.yml). +Venue: **Bioinformatics (OUP) — Application Note** (user-confirmed). + +Branch/PR: `cursor/paper-prep-venues-198e` / [#21](https://github.com/ehsanestaji/FastaGuard/pull/21). + +Control manifest: [`paper/submission.yml`](../paper/submission.yml). + Machine check: `cd paper && make check`. ## DONE (machine / repo complete) diff --git a/paper/README.md b/paper/README.md index 5b14711..e6758c1 100644 --- a/paper/README.md +++ b/paper/README.md @@ -1,6 +1,7 @@ # FastaGuard paper (LaTeX) -**Venue (user-confirmed):** [*Bioinformatics*](https://academic.oup.com/bioinformatics) — **Application Note** +**Venue (user-confirmed):** [*Bioinformatics*](https://academic.oup.com/bioinformatics) — **Application Note** + **PR:** [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · `cursor/paper-prep-venues-198e` Paper-prep only. Cold-start: [`HANDOFF.md`](HANDOFF.md). Readiness checklist: [`../docs/paper-readiness.md`](../docs/paper-readiness.md). diff --git a/paper/cover-letter.md b/paper/cover-letter.md index ce93424..1875f68 100644 --- a/paper/cover-letter.md +++ b/paper/cover-letter.md @@ -1,7 +1,9 @@ # Cover letter — *Bioinformatics* Application Note (draft) -**To:** The Editors, *Bioinformatics* (Oxford University Press) -**From:** Ehsan Estaji and Jian-Feng Mao +**To:** The Editors, *Bioinformatics* (Oxford University Press) + +**From:** Ehsan Estaji and Jian-Feng Mao + **Re:** Application Note submission — *FastaGuard: FASTA preflight QC for modern bioinformatics pipelines* Dear Editors, @@ -27,7 +29,7 @@ and abstract conventions (Summary / Availability and Implementation / Contact / Supplementary information). Source and documentation: https://github.com/ehsanestaji/FastaGuard -**Suggested topical area:** genome assembly / sequence analysis software tools +**Suggested topical area:** genome assembly / sequence analysis software tools (Editors may reassign as appropriate.) **Software availability.** The submitted release (v1.0.0) is open source (MIT), @@ -50,7 +52,10 @@ consideration at another journal, and that all authors approve submission. Thank you for your consideration. -Sincerely, -Ehsan Estaji -Jian-Feng Mao +Sincerely, + +Ehsan Estaji + +Jian-Feng Mao + Umeå Plant Science Centre, Department of Plant Physiology, Umeå University From 794ca83226c6bd9fa48a32eaf6fb9d2c1f391ca9 Mon Sep 17 00:00:00 2001 From: Ehsan ESTAJI <71376358+ehsanestaji@users.noreply.github.com> Date: Sat, 24 Oct 2026 20:55:51 +0200 Subject: [PATCH 8/9] feat(paper): add validator comparison and v1.0.0 evidence re-run; settle disclosures - Table 2 and paper/comparison/: capability comparison of py_fasta_validator 0.6, fa-lint 1.2.0, table2asn 1.29.324, SeqKit 2.14.0 and FastaGuard v1.0.0 on 30 author-constructed FASTA files (expectations fixed before running) and the two RefSeq assemblies; tool versions and SHA-256 checksums recorded. Generated results are byte-reproducible; the corpus favours FastaGuard and the paper says so. - docs/evidence/results/v1.0.0-rerun/: all five evidence-pack rows re-run with a build of the v1.0.0 tag; verdicts, gates, blockers, lengths, sequence counts, input SHA-256 and exit codes match the frozen v0.6.0 rows (timings differ). - AI-use disclosure rewritten per the authors (software and benchmarks first, manuscript drafted solely from them; Cursor agents and Claude Code named) in both the manuscript and the cover letter. Funding unchanged (matches the three related preprints); SNIC/HPC2N acknowledgement stays removed. - Record co-author confirmation (reported by the corresponding author), update the manifest and handoff docs, add the table2asn reference. Co-Authored-By: Claude Sonnet 5.5 --- .../v1.0.0-rerun/evidence_summary.json | 153 +++++ .../results/v1.0.0-rerun/evidence_summary.tsv | 6 + docs/paper-readiness.md | 7 +- paper/HANDOFF.md | 27 +- paper/comparison/real_results.tsv | 14 + paper/comparison/results.tsv | 184 ++++++ paper/comparison/run_comparison.py | 574 ++++++++++++++++++ paper/comparison/summary.tsv | 93 +++ paper/comparison/tool_versions.json | 53 ++ paper/cover-letter.md | 14 +- paper/main.tex | 86 ++- paper/references.bib | 8 + paper/submission.yml | 5 +- 13 files changed, 1195 insertions(+), 29 deletions(-) create mode 100644 docs/evidence/results/v1.0.0-rerun/evidence_summary.json create mode 100644 docs/evidence/results/v1.0.0-rerun/evidence_summary.tsv create mode 100644 paper/comparison/real_results.tsv create mode 100644 paper/comparison/results.tsv create mode 100644 paper/comparison/run_comparison.py create mode 100644 paper/comparison/summary.tsv create mode 100644 paper/comparison/tool_versions.json diff --git a/docs/evidence/results/v1.0.0-rerun/evidence_summary.json b/docs/evidence/results/v1.0.0-rerun/evidence_summary.json new file mode 100644 index 0000000..d89f7da --- /dev/null +++ b/docs/evidence/results/v1.0.0-rerun/evidence_summary.json @@ -0,0 +1,153 @@ +{ + "binary_sha256": "8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f", + "binary_to_source_reproducibility_attested": false, + "cases": [ + { + "accession": null, + "category": "synthetic", + "downstream_route": "If this fails, fix the FastaGuard run before comparing with QUAST, BUSCO, or other downstream tools.", + "elapsed_seconds": 0.0256, + "evidence_role": "offline valid FASTA smoke case", + "exit_code": 0, + "expected_scale": "tiny synthetic fixture", + "finding_count": 0, + "finding_ids": [], + "gate_blocking_findings": "", + "gate_mode": "pipeline", + "gate_status": "PASS", + "id": "synthetic_valid", + "input_bytes": 676, + "input_sha256": "71152ba646d1f2d077e0a3a185524c2138a0657ab62f5f0694709c15420c3e5d", + "label": "Deterministic synthetic FASTA", + "n50": 160, + "n90": 140, + "sequence_count": 4, + "source": "local", + "source_url": null, + "total_length": 604, + "verdict": "PASS" + }, + { + "accession": null, + "category": "fixture", + "downstream_route": "Fix FASTA-level blockers before QUAST, BUSCO, BlobToolKit, CheckM, annotation, or submission validators.", + "elapsed_seconds": 0.0304, + "evidence_role": "local blocker case for duplicate IDs, invalid characters, high-N records, and tiny contigs", + "exit_code": 0, + "expected_scale": "tiny intentionally problematic fixture", + "finding_count": 8, + "finding_ids": [ + "duplicate_ids", + "duplicate_first_token_ids", + "invalid_chars", + "high_n_rate", + "gap_runs", + "terminal_ns", + "length_outliers", + "composite_anomalies" + ], + "gate_blocking_findings": "duplicate_ids,duplicate_first_token_ids,invalid_chars,high_n_rate", + "gate_mode": "pipeline", + "gate_status": "FAIL", + "id": "problem_fixture", + "input_bytes": 187, + "input_sha256": "4b8551daeda739b62c8e7aaa1ebf300e4118167ca582c51eeccfa1549c96f0a5", + "label": "Problem assembly fixture", + "n50": 110, + "n90": 8, + "sequence_count": 5, + "source": "local", + "source_url": null, + "total_length": 145, + "verdict": "FAIL" + }, + { + "accession": null, + "category": "fixture", + "downstream_route": "If this passes, the pipeline can accept compressed FASTA before heavier QC tools.", + "elapsed_seconds": 0.025, + "evidence_role": "offline gzipped FASTA input smoke case", + "exit_code": 0, + "expected_scale": "tiny gzipped fixture", + "finding_count": 1, + "finding_ids": [ + "terminal_ns" + ], + "gate_blocking_findings": "", + "gate_mode": "pipeline", + "gate_status": "WARN", + "id": "gzipped_valid", + "input_bytes": 81, + "input_sha256": "544240d141ede11647a5cbdce60b23793dde1cc0559f2e918ecc80e9cc3fb48e", + "label": "Gzipped valid assembly fixture", + "n50": 16, + "n90": 15, + "sequence_count": 3, + "source": "local", + "source_url": null, + "total_length": 47, + "verdict": "WARN" + }, + { + "accession": "GCF_000005845.2", + "category": "bacterial", + "downstream_route": "If FastaGuard passes, route to QUAST for assembly metrics and BUSCO when biological completeness is in scope.", + "elapsed_seconds": 0.0885, + "evidence_role": "small bacterial reference assembly", + "exit_code": 0, + "expected_scale": "about 4.6 Mbp", + "finding_count": 0, + "finding_ids": [], + "gate_blocking_findings": "", + "gate_mode": "pipeline", + "gate_status": "PASS", + "id": "ecoli_k12_mg1655", + "input_bytes": 4699745, + "input_sha256": "53bb6a51b6e92139ced1e38f74b7938781027c52200922ff03718c2237d23bb4", + "label": "Escherichia coli K-12 MG1655", + "n50": 4641652, + "n90": 4641652, + "sequence_count": 1, + "source": "public_ncbi", + "source_url": "https://www.ncbi.nlm.nih.gov/datasets/genome/GCF_000005845.2/", + "total_length": 4641652, + "verdict": "PASS" + }, + { + "accession": "GCF_000182925.2", + "category": "fungal", + "downstream_route": "If FastaGuard passes, route to QUAST, BUSCO, and BlobToolKit-style follow-up when assembly quality or composition signals need deeper review.", + "elapsed_seconds": 0.8113, + "evidence_role": "small eukaryotic fungal reference assembly", + "exit_code": 0, + "expected_scale": "tens of Mbp", + "finding_count": 2, + "finding_ids": [ + "gap_runs", + "gap_pattern_warnings" + ], + "gate_blocking_findings": "", + "gate_mode": "pipeline", + "gate_status": "WARN", + "id": "neurospora_crassa_or74a", + "input_bytes": 41618285, + "input_sha256": "8176a4ac98fe05dffa4fa92df030ce0f15af23fbc382c8d8176ffedd3012d847", + "label": "Neurospora crassa OR74A", + "n50": 6000761, + "n90": 4218384, + "sequence_count": 21, + "source": "public_ncbi", + "source_url": "https://www.ncbi.nlm.nih.gov/datasets/genome/GCF_000182925.2/", + "total_length": 41102378, + "verdict": "WARN" + } + ], + "fastaguard_version": "1.0.0", + "generated_at": "2026-10-24T18:11:39+00:00", + "platform": "macOS-26.7.1-arm64-arm-64bit-Mach-O", + "provenance_scope": "source_commit records verified release-tag and source-tree equality. fastaguard_version and binary_sha256 record the observed executable. Binary-to-source reproducibility was not independently attested.", + "python": "3.14.5", + "runtime_context": "Elapsed seconds are contextual measurements from one local run, not cross-platform performance guarantees.", + "schema_version": 1, + "source_commit": "103ee343737e7f4d5719a6f8bda7c05c4676e9a9" +} diff --git a/docs/evidence/results/v1.0.0-rerun/evidence_summary.tsv b/docs/evidence/results/v1.0.0-rerun/evidence_summary.tsv new file mode 100644 index 0000000..ca830ad --- /dev/null +++ b/docs/evidence/results/v1.0.0-rerun/evidence_summary.tsv @@ -0,0 +1,6 @@ +schema_version generated_at fastaguard_version source_commit binary_sha256 provenance_scope binary_to_source_reproducibility_attested platform python runtime_context id label category source accession source_url evidence_role expected_scale downstream_route input_bytes input_sha256 elapsed_seconds exit_code verdict gate_mode gate_status gate_blocking_findings sequence_count total_length n50 n90 finding_ids finding_count +1 2026-10-24T18:11:39+00:00 1.0.0 103ee343737e7f4d5719a6f8bda7c05c4676e9a9 8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f source_commit records verified release-tag and source-tree equality. fastaguard_version and binary_sha256 record the observed executable. Binary-to-source reproducibility was not independently attested. false macOS-26.7.1-arm64-arm-64bit-Mach-O 3.14.5 Elapsed seconds are contextual measurements from one local run, not cross-platform performance guarantees. synthetic_valid Deterministic synthetic FASTA synthetic local offline valid FASTA smoke case tiny synthetic fixture If this fails, fix the FastaGuard run before comparing with QUAST, BUSCO, or other downstream tools. 676 71152ba646d1f2d077e0a3a185524c2138a0657ab62f5f0694709c15420c3e5d 0.0256 0 PASS pipeline PASS 4 604 160 140 0 +1 2026-10-24T18:11:39+00:00 1.0.0 103ee343737e7f4d5719a6f8bda7c05c4676e9a9 8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f source_commit records verified release-tag and source-tree equality. fastaguard_version and binary_sha256 record the observed executable. Binary-to-source reproducibility was not independently attested. false macOS-26.7.1-arm64-arm-64bit-Mach-O 3.14.5 Elapsed seconds are contextual measurements from one local run, not cross-platform performance guarantees. problem_fixture Problem assembly fixture fixture local local blocker case for duplicate IDs, invalid characters, high-N records, and tiny contigs tiny intentionally problematic fixture Fix FASTA-level blockers before QUAST, BUSCO, BlobToolKit, CheckM, annotation, or submission validators. 187 4b8551daeda739b62c8e7aaa1ebf300e4118167ca582c51eeccfa1549c96f0a5 0.0304 0 FAIL pipeline FAIL duplicate_ids,duplicate_first_token_ids,invalid_chars,high_n_rate 5 145 110 8 duplicate_ids,duplicate_first_token_ids,invalid_chars,high_n_rate,gap_runs,terminal_ns,length_outliers,composite_anomalies 8 +1 2026-10-24T18:11:39+00:00 1.0.0 103ee343737e7f4d5719a6f8bda7c05c4676e9a9 8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f source_commit records verified release-tag and source-tree equality. fastaguard_version and binary_sha256 record the observed executable. Binary-to-source reproducibility was not independently attested. false macOS-26.7.1-arm64-arm-64bit-Mach-O 3.14.5 Elapsed seconds are contextual measurements from one local run, not cross-platform performance guarantees. gzipped_valid Gzipped valid assembly fixture fixture local offline gzipped FASTA input smoke case tiny gzipped fixture If this passes, the pipeline can accept compressed FASTA before heavier QC tools. 81 544240d141ede11647a5cbdce60b23793dde1cc0559f2e918ecc80e9cc3fb48e 0.025 0 WARN pipeline WARN 3 47 16 15 terminal_ns 1 +1 2026-10-24T18:11:39+00:00 1.0.0 103ee343737e7f4d5719a6f8bda7c05c4676e9a9 8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f source_commit records verified release-tag and source-tree equality. fastaguard_version and binary_sha256 record the observed executable. Binary-to-source reproducibility was not independently attested. false macOS-26.7.1-arm64-arm-64bit-Mach-O 3.14.5 Elapsed seconds are contextual measurements from one local run, not cross-platform performance guarantees. ecoli_k12_mg1655 Escherichia coli K-12 MG1655 bacterial public_ncbi GCF_000005845.2 https://www.ncbi.nlm.nih.gov/datasets/genome/GCF_000005845.2/ small bacterial reference assembly about 4.6 Mbp If FastaGuard passes, route to QUAST for assembly metrics and BUSCO when biological completeness is in scope. 4699745 53bb6a51b6e92139ced1e38f74b7938781027c52200922ff03718c2237d23bb4 0.0885 0 PASS pipeline PASS 1 4641652 4641652 4641652 0 +1 2026-10-24T18:11:39+00:00 1.0.0 103ee343737e7f4d5719a6f8bda7c05c4676e9a9 8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f source_commit records verified release-tag and source-tree equality. fastaguard_version and binary_sha256 record the observed executable. Binary-to-source reproducibility was not independently attested. false macOS-26.7.1-arm64-arm-64bit-Mach-O 3.14.5 Elapsed seconds are contextual measurements from one local run, not cross-platform performance guarantees. neurospora_crassa_or74a Neurospora crassa OR74A fungal public_ncbi GCF_000182925.2 https://www.ncbi.nlm.nih.gov/datasets/genome/GCF_000182925.2/ small eukaryotic fungal reference assembly tens of Mbp If FastaGuard passes, route to QUAST, BUSCO, and BlobToolKit-style follow-up when assembly quality or composition signals need deeper review. 41618285 8176a4ac98fe05dffa4fa92df030ce0f15af23fbc382c8d8176ffedd3012d847 0.8113 0 WARN pipeline WARN 21 41102378 6000761 4218384 gap_runs,gap_pattern_warnings 2 diff --git a/docs/paper-readiness.md b/docs/paper-readiness.md index e03cb25..e2c986c 100644 --- a/docs/paper-readiness.md +++ b/docs/paper-readiness.md @@ -25,6 +25,9 @@ Machine check: `cd paper && make check`. | Upstream claims verified (Bioconda, BioContainers, nf-core module, Snakemake wrapper) | DONE | | Pre-submission review: claims re-tested against a v1.0.0 build; related work + references audited; fixes applied (see `paper/HANDOFF.md`) | DONE | | Zenodo record creators corrected (Estaji + Mao, ORCIDs, affiliation) | DONE | +| Validator comparison vs py_fasta_validator, fa-lint, table2asn, SeqKit (Table 2, `paper/comparison/`) | DONE | +| NCBI rows re-run with a v1.0.0 build (`docs/evidence/results/v1.0.0-rerun/`) | DONE | +| AI-disclosure wording, funding line, SNIC acknowledgement settled with the author | DONE | | bioRxiv build target + checklist | DONE | | PDF builds (`make`) | DONE | | Word count ~2.0–2.6k band | DONE (~2.0k+) | @@ -33,8 +36,8 @@ Machine check: `cd paper && make check`. | Item | Notes | | --- | --- | -| Co-author final OK (Jian-Feng Mao) | Confirm authorship/order/CRediT | -| Review decisions | AI-disclosure wording, funding line, SNIC ack, optional feature matrix / differential test, optional NCBI re-run at v1.0.0 (see `paper/HANDOFF.md`) | +| Co-author final OK (Jian-Feng Mao) | DONE 2026-10-24: reported in chat by Ehsan Estaji (Jian-Feng agreed and knew of the work); no written message seen by the agent | +| Merge PR #21 | `paper/comparison/` is not in the v1.0.0 archive; merge before submission so paths in the paper resolve | | bioRxiv deposit | Follow [`paper/biorxiv-checklist.md`](../paper/biorxiv-checklist.md); all authors must consent; cover letter discloses it | | ScholarOne upload | Format-free first submission OK; upload PDF + cover letter; OUP General Template deferred to **revision** | | APC / Read & Publish / ISCB discount | Check before acceptance | diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md index 71e7820..19a48da 100644 --- a/paper/HANDOFF.md +++ b/paper/HANDOFF.md @@ -10,7 +10,7 @@ PR: [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · branch `cursor/p | --- | --- | | **DONE / submit-package ready** | Full draft; front-matter; evidence freeze; `submission.yml`; `check_submission.py`; cover letter; `.zenodo.json`; bioRxiv build target; `docs/paper-readiness.md` | | **DONE (2026-10-24)** | Zenodo DOI minted and pasted into Availability + manifest; OUP guideline review (AI + preprint disclosure added to cover letter); external claims verified (see below); PDF metadata + Fig. 1 label overlap fixed | -| **USER-ACTION only** | Co-author final OK (Jian-Feng Mao); bioRxiv deposit; ScholarOne upload; APC/R&P/ISCB | +| **USER-ACTION only** | bioRxiv deposit; ScholarOne upload; APC/R&P/ISCB | Do **not** claim bioRxiv or ScholarOne submitted from this agent track until the confirmation page / DOI is seen. Never click final Submit without explicit user OK. @@ -73,11 +73,26 @@ references) plus an editorial pass. Applied to `main.tex` / `references.bib`: - Zenodo record 23622311 creators fixed on zenodo.org (Estaji + Mao, ORCIDs, UPSC affiliation); DOI unchanged. - PDF: `--flag` ligature fixed (microtype `\DisableLigatures`), PDF metadata filled, Fig. 1 label overlap fixed. -Open items (user decisions, NOT done): AI-disclosure wording vs OUP rule; funding line confirmation; -restore SNIC ack only if HPC2N was really used; optional feature-matrix + differential test vs -py_fasta_validator/fa-lint/table2asn (referee will likely ask); optional re-run of the two NCBI rows with v1.0.0; -`CITATION.cff` lists Estaji only (product repo); Zenodo description still says Bioconda "remains at v0.7.0". -Length: body ~2,270 words (limit ~2,600) but 24 references push the draft PDF to 7 pages. +Resolved after the user's answers (same day): +- AI disclosure rewritten (paper + cover letter): software, code base and benchmarking results came first; the draft + was written solely from them, not from a free-form prompt; tools named (Cursor-based agents, Anthropic Claude Code). +- Funding unchanged: GraphPop, GraphMana and VariantFlow preprints carry the identical WIFORCE/KAW sentence. +- SNIC/HPC2N not used for FastaGuard: acknowledgement stays removed. +- NCBI rows re-run with a v1.0.0 build (clean clone at the tag + NCBI Datasets 18.39.0): all five evidence-pack rows + match the frozen v0.6.0 values on verdict, gate, blocking findings, lengths, sequence counts, SHA-256, exit code + (timings differ). Portable summary: `docs/evidence/results/v1.0.0-rerun/`. Binary was built from the v1.0.0 tag, not + the release asset. +- Validator comparison added (Table 2, `paper/comparison/`): py_fasta_validator 0.6, fa-lint 1.2.0, table2asn 1.29.324, + SeqKit 2.14.0, FastaGuard 1.0.0 on 30 constructed files (expectations fixed first) + the two RefSeq assemblies. + Reproduce with `python3 -I paper/comparison/run_comparison.py --help`; tool SHA-256s in `tool_versions.json`. + Author-constructed corpus favours FastaGuard; capability, not accuracy. A first real-data run was discarded (label + collision + stale report read); the script was fixed and the 30-file results were byte-identical afterwards. + +Co-author OK: reported by Ehsan in chat 2026-10-24 (no message from Jian-Feng seen by the agent). +Still open: merge PR #21 so `paper/comparison/` resolves on main (it is not in the v1.0.0 +Zenodo archive; consider a small separate deposit for test data); `CITATION.cff` lists Estaji only (product repo); Zenodo +description still says Bioconda "remains at v0.7.0"; length (Introduction-Discussion ~2,260 words; ~2,700 incl. +abstract, captions and declarations; ~850 more in references; draft PDF is 8 pages). ## OUP policy points that shaped the cover letter diff --git a/paper/comparison/real_results.tsv b/paper/comparison/real_results.tsv new file mode 100644 index 0000000..679a576 --- /dev/null +++ b/paper/comparison/real_results.tsv @@ -0,0 +1,14 @@ +# Real files: no expectation, nothing scored. Same decision rules as results.tsv. +file scope expectation tool flagged strict exit_code detail +ecoli_GCF_000005845.2 real n/a py_fasta_validator no no 0 valid +ecoli_GCF_000005845.2 real n/a fa-lint no no 0 Fasta is valid: $REAL/genomic.fna +ecoli_GCF_000005845.2 real n/a table2asn no no 0 only baseline warnings (2) +ecoli_GCF_000005845.2 real n/a seqkit no no 0 no message +ecoli_GCF_000005845.2 real n/a fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +ecoli_GCF_000005845.2 real n/a fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +neurospora_GCF_000182925.2 real n/a py_fasta_validator no no 0 valid +neurospora_GCF_000182925.2 real n/a fa-lint no no 0 Fasta is valid: $REAL/genomic.fna +neurospora_GCF_000182925.2 real n/a table2asn yes yes 0 error [SEQ_INST.InternalNsInSeqRaw] Run of 100 Ns in raw sequence starting at base 72205 BIOSEQ: lcl|NC_02; error [SEQ_INST.InternalNsInSeqRaw] Run of 100 Ns in raw sequence starting at base 77053 BIOSEQ: lcl|NC_02; error [SEQ_INST.InternalNsInSeqRaw] Run of 1 +neurospora_GCF_000182925.2 real n/a seqkit no no 0 no message +neurospora_GCF_000182925.2 real n/a fastaguard_pipeline yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=gap_runs,gap_pattern_warnings +neurospora_GCF_000182925.2 real n/a fastaguard_ncbi yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=gap_runs,gap_pattern_warnings diff --git a/paper/comparison/results.tsv b/paper/comparison/results.tsv new file mode 100644 index 0000000..386a7f1 --- /dev/null +++ b/paper/comparison/results.tsv @@ -0,0 +1,184 @@ +# NOTE: the corpus is author-constructed and favours FastaGuard's own check list. +# Expectations (valid/error/advisory) were fixed before any tool ran; advisory rows are not scored. +# The tools were not designed for the same purpose. flagged: yes|no|crash|n.a.; strict: blocked / error-level. +file scope expectation tool flagged strict exit_code detail +valid_simple control valid py_fasta_validator no no 0 valid +valid_simple control valid fa-lint no no 0 Fasta is valid: $CORPUS/valid_simple.fa +valid_simple control valid table2asn no no 0 only baseline warnings (4) +valid_simple control valid seqkit no no 0 no message +valid_simple control valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_simple control valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_single_line control valid py_fasta_validator no no 0 valid +valid_single_line control valid fa-lint no no 0 Fasta is valid: $CORPUS/valid_single_line.fa +valid_single_line control valid table2asn no no 0 only baseline warnings (4) +valid_single_line control valid seqkit no no 0 no message +valid_single_line control valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_single_line control valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_softmasked control valid py_fasta_validator no no 0 valid +valid_softmasked control valid fa-lint no no 0 Fasta is valid: $CORPUS/valid_softmasked.fa +valid_softmasked control valid table2asn no no 0 only baseline warnings (4) +valid_softmasked control valid seqkit no no 0 no message +valid_softmasked control valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_softmasked control valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_iupac control valid py_fasta_validator no no 0 valid +valid_iupac control valid fa-lint no no 0 Fasta is valid: $CORPUS/valid_iupac.fa +valid_iupac control valid table2asn no no 0 only baseline warnings (4) +valid_iupac control valid seqkit no no 0 no message +valid_iupac control valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_iupac control valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_no_final_newline control valid py_fasta_validator no no 0 valid +valid_no_final_newline control valid fa-lint no no 0 Fasta is valid: $CORPUS/valid_no_final_newline.fa +valid_no_final_newline control valid table2asn no no 0 only baseline warnings (4) +valid_no_final_newline control valid seqkit no no 0 no message +valid_no_final_newline control valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +valid_no_final_newline control valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +empty_file syntax error py_fasta_validator no no 0 valid +empty_file syntax error fa-lint yes yes 1 Fasta file is empty +empty_file syntax error table2asn crash crash 1 console: Cannot open $WORK/table2asn/empty_file/empty_file.fa +empty_file syntax error seqkit no no 0 no message +empty_file syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +empty_file syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +seq_before_header syntax error py_fasta_validator yes yes 1 $CORPUS/seq_before_header.fa does not start with a > +seq_before_header syntax error fa-lint yes yes 1 Fasta file must start with a header line +seq_before_header syntax error table2asn yes yes 1 console Error: CFastaReader: Input doesn't start with a defline or comment around line 1 +seq_before_header syntax error seqkit yes yes 255 [ERRO] fastx: invalid FASTA/Q format +seq_before_header syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +seq_before_header syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +empty_header syntax error py_fasta_validator no no 0 valid +empty_header syntax error fa-lint yes yes 1 Fasta header does not contain a valid ID near line #12: > +empty_header syntax error table2asn no no 0 only baseline warnings (4) +empty_header syntax error seqkit yes yes 255 [ERRO] seq: invalid DNAredundant letter: +empty_header syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +empty_header syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +empty_record syntax error py_fasta_validator no no 0 valid +empty_record syntax error fa-lint yes yes 1 Empty sequence for record near line #12: ctg2_empty +empty_record syntax error table2asn yes yes 1 console Error: FASTA-Reader: No residues given +empty_record syntax error seqkit no no 0 no message +empty_record syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +empty_record syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +dup_id_identical_header identifier error py_fasta_validator yes yes 2 $CORPUS/dup_id_identical_header.fa has multiple sequences with the same identifier +dup_id_identical_header identifier error fa-lint yes yes 1 Duplicate fasta ID found near line #12: >ctg1 isolate X +dup_id_identical_header identifier error table2asn crash crash - timeout after 30s (process killed) +dup_id_identical_header identifier error seqkit no no 0 no message +dup_id_identical_header identifier error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=duplicate_ids,duplicate_first_token_ids advisory=none +dup_id_identical_header identifier error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=duplicate_ids,duplicate_first_token_ids advisory=none +dup_first_token_id identifier error py_fasta_validator yes yes 2 $CORPUS/dup_first_token_id.fa has multiple sequences with the same identifier +dup_first_token_id identifier error fa-lint yes yes 1 Duplicate fasta ID found near line #12: >ctg1 isolate B +dup_first_token_id identifier error table2asn crash crash - timeout after 30s (process killed) +dup_first_token_id identifier error seqkit no no 0 no message +dup_first_token_id identifier error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=duplicate_ids,duplicate_first_token_ids advisory=none +dup_first_token_id identifier error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=duplicate_ids,duplicate_first_token_ids advisory=none +dup_sequence_diff_ids content advisory py_fasta_validator no no 0 valid +dup_sequence_diff_ids content advisory fa-lint no no 0 Fasta is valid: $CORPUS/dup_sequence_diff_ids.fa +dup_sequence_diff_ids content advisory table2asn no no 0 only baseline warnings (4) +dup_sequence_diff_ids content advisory seqkit no no 0 no message +dup_sequence_diff_ids content advisory fastaguard_pipeline yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=duplicate_sequences +dup_sequence_diff_ids content advisory fastaguard_ncbi yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=duplicate_sequences +digit_in_sequence syntax error py_fasta_validator yes yes 4 $CORPUS/digit_in_sequence.fa has non-sequence characters in it +digit_in_sequence syntax error fa-lint yes yes 1 Invalid sequence character near line #7 +digit_in_sequence syntax error table2asn yes yes 1 console Error: CFastaReader: There are invalid residue(s) in input sequence +digit_in_sequence syntax error seqkit yes yes 255 [ERRO] seq: invalid DNAredundant letter: 7 +digit_in_sequence syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +digit_in_sequence syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +stop_star_in_sequence syntax error py_fasta_validator yes yes 4 $CORPUS/stop_star_in_sequence.fa has non-sequence characters in it +stop_star_in_sequence syntax error fa-lint yes yes 1 Invalid sequence character near line #7 +stop_star_in_sequence syntax error table2asn yes yes 1 console Error: CFastaReader: There are invalid residue(s) in input sequence +stop_star_in_sequence syntax error seqkit yes yes 255 [ERRO] seq: invalid DNAredundant letter: * +stop_star_in_sequence syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +stop_star_in_sequence syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +gap_dash_in_sequence syntax advisory py_fasta_validator yes yes 4 $CORPUS/gap_dash_in_sequence.fa has non-sequence characters in it +gap_dash_in_sequence syntax advisory fa-lint yes yes 1 Invalid sequence character near line #7 +gap_dash_in_sequence syntax advisory table2asn yes no 2 console Warning: CFastaReader: Hyphens are invalid and will be ignored around line 7 +gap_dash_in_sequence syntax advisory seqkit no no 0 no message +gap_dash_in_sequence syntax advisory fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +gap_dash_in_sequence syntax advisory fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +non_iupac_letter_x syntax error py_fasta_validator no no 0 valid +non_iupac_letter_x syntax error fa-lint no no 0 Fasta is valid: $CORPUS/non_iupac_letter_x.fa +non_iupac_letter_x syntax error table2asn yes yes 1 console Error: CFastaReader: There are invalid residue(s) in input sequence +non_iupac_letter_x syntax error seqkit yes yes 255 [ERRO] seq: invalid DNAredundant letter: X +non_iupac_letter_x syntax error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +non_iupac_letter_x syntax error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +space_in_sequence syntax advisory py_fasta_validator yes yes 4 $CORPUS/space_in_sequence.fa has non-sequence characters in it +space_in_sequence syntax advisory fa-lint yes yes 1 Invalid sequence character near line #2 +space_in_sequence syntax advisory table2asn no no 0 only baseline warnings (2) +space_in_sequence syntax advisory seqkit no no 0 no message +space_in_sequence syntax advisory fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +space_in_sequence syntax advisory fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_chars advisory=none +blank_line_in_record syntax advisory py_fasta_validator no no 0 valid +blank_line_in_record syntax advisory fa-lint yes yes 1 Empty sequence line near line #4 +blank_line_in_record syntax advisory table2asn no no 0 only baseline warnings (2) +blank_line_in_record syntax advisory seqkit no no 0 no message +blank_line_in_record syntax advisory fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +blank_line_in_record syntax advisory fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +crlf_line_endings syntax advisory py_fasta_validator no no 0 valid +crlf_line_endings syntax advisory fa-lint no no 0 Fasta is valid: $CORPUS/crlf_line_endings.fa +crlf_line_endings syntax advisory table2asn no no 0 only baseline warnings (4) +crlf_line_endings syntax advisory seqkit no no 0 no message +crlf_line_endings syntax advisory fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +crlf_line_endings syntax advisory fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +inconsistent_wrapping syntax advisory py_fasta_validator no no 0 valid +inconsistent_wrapping syntax advisory fa-lint yes yes 1 Sequence near line #4 violates preceding line wrapping length +inconsistent_wrapping syntax advisory table2asn no no 0 only baseline warnings (2) +inconsistent_wrapping syntax advisory seqkit no no 0 no message +inconsistent_wrapping syntax advisory fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +inconsistent_wrapping syntax advisory fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +utf8_bom syntax advisory py_fasta_validator yes yes 1 $CORPUS/utf8_bom.fa does not start with a > +utf8_bom syntax advisory fa-lint yes yes 1 Fasta file must start with a header line +utf8_bom syntax advisory table2asn yes yes 1 console Error: Object type not supported +utf8_bom syntax advisory seqkit no no 0 no message +utf8_bom syntax advisory fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +utf8_bom syntax advisory fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=invalid_fasta_structure advisory=none +high_n_record content advisory py_fasta_validator no no 0 valid +high_n_record content advisory fa-lint no no 0 Fasta is valid: $CORPUS/high_n_record.fa +high_n_record content advisory table2asn yes yes 2 error [SEQ_INST.ContigsTooShort] Maximum contig length is 2 bases BIOSEQ: lcl|ctg1: raw, dna len= 600 +high_n_record content advisory seqkit no no 0 no message +high_n_record content advisory fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=high_n_rate advisory=none +high_n_record content advisory fastaguard_ncbi yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=high_n_rate +all_n_record content error py_fasta_validator no no 0 valid +all_n_record content error fa-lint yes yes 1 Sequence near line #1 is comprised entirely of N, n, '.' or '*': ctg1 +all_n_record content error table2asn yes yes 2 critical [SEQ_INST.AllNs] Sequence is all Ns BIOSEQ: lcl|ctg1: raw, dna len= 600 +all_n_record content error seqkit no no 0 no message +all_n_record content error fastaguard_pipeline yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=high_n_rate advisory=gap_runs,terminal_ns,composite_anomalies +all_n_record content error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=terminal_ns advisory=high_n_rate,gap_runs,composite_anomalies +tiny_contig_50 content advisory py_fasta_validator no no 0 valid +tiny_contig_50 content advisory fa-lint no no 0 Fasta is valid: $CORPUS/tiny_contig_50.fa +tiny_contig_50 content advisory table2asn no no 0 only baseline warnings (4) +tiny_contig_50 content advisory seqkit no no 0 no message +tiny_contig_50 content advisory fastaguard_pipeline yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=tiny_contigs +tiny_contig_50 content advisory fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=ncbi_genome_short_contigs advisory=tiny_contigs +seqid_len_49 ncbi valid py_fasta_validator no no 0 valid +seqid_len_49 ncbi valid fa-lint no no 0 Fasta is valid: $CORPUS/seqid_len_49.fa +seqid_len_49 ncbi valid table2asn no no 0 only baseline warnings (2) +seqid_len_49 ncbi valid seqkit no no 0 no message +seqid_len_49 ncbi valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +seqid_len_49 ncbi valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +seqid_len_50 ncbi error py_fasta_validator no no 0 valid +seqid_len_50 ncbi error fa-lint no no 0 Fasta is valid: $CORPUS/seqid_len_50.fa +seqid_len_50 ncbi error table2asn no no 0 only baseline warnings (2) +seqid_len_50 ncbi error seqkit no no 0 no message +seqid_len_50 ncbi error fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +seqid_len_50 ncbi error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=ncbi_genome_seqid advisory=none +seqid_disallowed_char ncbi error py_fasta_validator no no 0 valid +seqid_disallowed_char ncbi error fa-lint no no 0 Fasta is valid: $CORPUS/seqid_disallowed_char.fa +seqid_disallowed_char ncbi error table2asn no no 0 only baseline warnings (2) +seqid_disallowed_char ncbi error seqkit no no 0 no message +seqid_disallowed_char ncbi error fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +seqid_disallowed_char ncbi error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=ncbi_genome_seqid advisory=none +contig_len_199 ncbi advisory py_fasta_validator no no 0 valid +contig_len_199 ncbi advisory fa-lint no no 0 Fasta is valid: $CORPUS/contig_len_199.fa +contig_len_199 ncbi advisory table2asn no no 0 only baseline warnings (2) +contig_len_199 ncbi advisory seqkit no no 0 no message +contig_len_199 ncbi advisory fastaguard_pipeline yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=tiny_contigs +contig_len_199 ncbi advisory fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=ncbi_genome_short_contigs advisory=tiny_contigs +contig_len_200 ncbi valid py_fasta_validator no no 0 valid +contig_len_200 ncbi valid fa-lint no no 0 Fasta is valid: $CORPUS/contig_len_200.fa +contig_len_200 ncbi valid table2asn no no 0 only baseline warnings (2) +contig_len_200 ncbi valid seqkit no no 0 no message +contig_len_200 ncbi valid fastaguard_pipeline no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +contig_len_200 ncbi valid fastaguard_ncbi no no 0 verdict=PASS gate=PASS can_continue=true blocking=none advisory=none +terminal_ns ncbi error py_fasta_validator no no 0 valid +terminal_ns ncbi error fa-lint no no 0 Fasta is valid: $CORPUS/terminal_ns.fa +terminal_ns ncbi error table2asn yes yes 0 error [SEQ_INST.TerminalNs] N at beginning of sequence BIOSEQ: lcl|ctg1: raw, dna len= 600; error [SEQ_INST.TerminalNs] N at end of sequence BIOSEQ: lcl|ctg1: raw, dna len= 600 +terminal_ns ncbi error seqkit no no 0 no message +terminal_ns ncbi error fastaguard_pipeline yes no 0 verdict=WARN gate=WARN can_continue=true blocking=none advisory=terminal_ns +terminal_ns ncbi error fastaguard_ncbi yes yes 0 verdict=FAIL gate=FAIL can_continue=false blocking=terminal_ns advisory=none diff --git a/paper/comparison/run_comparison.py b/paper/comparison/run_comparison.py new file mode 100644 index 0000000..d9e92dc --- /dev/null +++ b/paper/comparison/run_comparison.py @@ -0,0 +1,574 @@ +#!/usr/bin/env python3 +"""Capability comparison of FASTA validators on a small synthetic corpus. + +HONEST SCOPE. The corpus is AUTHOR-CONSTRUCTED and was designed around the check list of +FastaGuard (a FASTA preflight-QC CLI), so it favours FastaGuard. The tools were NOT built +for the same purpose: py_fasta_validator is a minimal syntax checker, fa-lint a protein/ +nucleotide FASTA linter, table2asn an NCBI submission converter whose validator is a side +effect, SeqKit a general toolkit whose `seq -v` checks residue letters. Expectations +(valid / error / advisory) were written down BEFORE any tool was run; advisory rows are +reported but never scored. Grounding: https://www.ncbi.nlm.nih.gov/genbank/fastaformat/ +(F) and https://www.ncbi.nlm.nih.gov/genbank/genomesubmit/ (G), read 2026-10-24. + +USAGE (stdlib only, Python 3.9+): + run_comparison.py --corpus-dir DIR [--out-dir DIR] [--work-dir DIR] [--timeout SEC] + [--fastaguard P] [--py-fasta-validator P] [--fa-lint P] [--table2asn P] [--seqkit P] + [--archives DIR] [--generate-only] [--real [LABEL=]FASTA [[LABEL=]FASTA ...]] + [--real-timeout SEC] +Tool location: flag, else env FASTAGUARD / PY_FASTA_VALIDATOR / FA_LINT / TABLE2ASN / +SEQKIT, else the same-named executable on PATH. A missing tool is reported as n.a. +--out-dir defaults to this script's directory (results.tsv, summary.tsv, +tool_versions.json, plus real_results.tsv with --real). --generate-only writes the corpus, +prints name/scope/expectation/sha256/rationale as TSV and exits. --archives DIR re-hashes +downloaded archives found anywhere below DIR against the pinned SHA-256 values below. +--real runs the same tools on real files (no expectation) into real_results.tsv; give each +file a unique LABEL=PATH (the label defaults to the file name) and a longer --real-timeout. + +DECISION RULES (flagged = yes | no | crash | n.a.; strict is a second, stricter level) + py_fasta_validator -f F: exit 1/2/4 (documented codes) -> yes; 0 -> no; Traceback, + signal, timeout or any other code -> crash. Exit code recorded. + fa-lint -fasta F: exit != 0 or a logrus level=error|fatal|warn|panic on stderr -> yes; + "panic:"/"goroutine" in stderr, signal or timeout -> crash. + seqkit seq -v -t dna F -o /dev/null: exit != 0 or [ERRO]/[WARN] on stderr -> yes; + panic text, signal or timeout -> crash. (-t dna is explicit: with -t auto the + alphabet is guessed from a prefix and tiny inputs are not validated.) + table2asn: repo invocation of scripts/verify_ncbi_genome_policy.py (-t synthetic + template -M n -j "[organism=synthetic construct][moltype=genomic DNA]" -V vb -Z). + Every file gets two template-induced baseline warnings, so a bare "warning" is not + attributable to the file. flagged = yes if (a) a console Error/Fatal/Critical block + (FASTA reader rejections print there, exit 1, no .val), or (b) a .val/.stats + message of level Error/Critical/Fatal/Reject (the repo's "rejected"), or (c) a + Warning whose rule code is absent from the valid_simple control run, or a console + Warning block with a Problem line (FASTA-reader warnings; added after the first run, + it only affects gap_dash_in_sequence). strict = yes only for (a)/(b). Non-zero exit + without such evidence, a console "Cannot open" (what it prints for an empty file), + signal, timeout (it hangs on duplicate SeqIDs: idle, >240 s checked by hand), or no + artifact at all -> crash (repo: tool_error). + fastaguard_pipeline: --profile assembly --gate pipeline; fastaguard_ncbi: --profile + assembly --gate submission --submission-target ncbi; defaults otherwise (no + --min-contig-length). flagged = verdict.status != PASS; strict = not + gate.can_continue ("blocked"). Missing/invalid JSON -> crash. FastaGuard exits 0 + even for FAIL, so the exit code carries no verdict. +For non-layered tools strict == flagged. Scoring: error rows -> flagged is TP (else miss), +valid rows -> flagged is FP (else TN), crash rows are counted separately, advisory rows +are reported only. Per-scope tallies: syntax, identifier, content, ncbi, control. +results.tsv columns: file, scope, expectation, tool, flagged, strict, exit_code, detail. +Reproduce: see tool_versions.json (pinned archives) and the command in the final report. +""" + +import argparse +import hashlib +import json +import os +import platform +import re +import shutil +import subprocess +import sys +import tempfile +import zlib +from pathlib import Path + +TWO_LEVEL = {"table2asn", "fastaguard_pipeline", "fastaguard_ncbi"} +TOOLS = ["py_fasta_validator", "fa-lint", "table2asn", "seqkit", + "fastaguard_pipeline", "fastaguard_ncbi"] +ANSI = re.compile(r"\x1b\[[0-9;]*m") +ERR_RE = re.compile(r"(?i)(?:^|[\s\[])(?:FATAL|CRITICAL|ERROR)(?:\]|\s*:)") # from the repo script +VAL_RE = re.compile(r"^\s*(\w+):\s+\S+\s+\[([^\]]+)\]\s*(.*)$") +ERR_LEVELS = {"error", "critical", "fatal", "reject", "rejected"} +BAD_FALINT = re.compile(r"level=(?:error|fatal|warn|warning|panic)", re.I) +T2A_ARGS = ["-M", "n", "-j", "[organism=synthetic construct][moltype=genomic DNA]", "-V", "vb", "-Z"] +T2A_TEMPLATE = """Submit-block ::= { + contact { + contact { + name name { + last "Validation", + first "Local" + } + } + }, + cit { + authors { + names std { + { + name name { + last "Validation", + first "Local", + initials "L." + } + } + }, + affil std { + affil "Controlled local validation", + city "Brussels", + country "Belgium" + } + } + }, + subtype new +} + +Seqdesc ::= pub { + pub { + gen { + cit "unpublished", + authors { + names std { + { + name name { + last "Validation", + first "Local", + initials "L." + } + } + } + }, + title "Controlled FASTA validation fixture" + } + } +} +""" +# Pinned archives (macOS arm64 builds). Digests were checked at download time against the +# publisher's own value (GitHub release digest / PyPI JSON / repo evidence for table2asn). +SOURCES = { + "py_fasta_validator": ("https://files.pythonhosted.org/packages/source/p/py_fasta_validator/" + "py_fasta_validator-0.6.tar.gz", + "ca29c20e1be31ef190f923b62ddb7fccda8c1c3ba9cf699a6ccaccce10ea011a", + ["-v"], "PyPI sdist of project py-fasta-validator; built in a python3.11 venv " + "with setuptools==80.9.0 because the shipped CLI imports pkg_resources"), + "fa-lint": ("https://github.com/GallVp/fa-lint/releases/download/1.2.0/fa-lint-darwin-arm64.tar.gz", + "6edcb792489b18bcfd03156912d05227ac05cdf657c517041c40489566470436", + ["-version"], "official release binary"), + "table2asn": ("https://ftp.ncbi.nlm.nih.gov/asn1-converters/by_program/table2asn/mac.table2asn.gz", + "348b9d5f1b05065f4e5e61b6c2350e5ff044de2d6dc14765be6ac02c00e59341", + ["-version"], "digest equals the one pinned in docs/evidence/" + "fastaguard-v0.7-ncbi-genome-policy.md; NCBI publishes no separate digest"), + "seqkit": ("https://github.com/shenwei356/seqkit/releases/download/v2.14.0/seqkit_darwin_arm64.tar.gz", + "7b3a6a7c9be07a06c5b308d1aad769c09762b36b387690eed8b91b00d01503b7", + ["version"], "official release binary; md5 2b335eb27f69af97a252818a85145684 also matched"), + "fastaguard": ("local build of `git archive v1.0.0` of https://github.com/ehsanestaji/FastaGuard", + None, ["--version"], "no archive; executable hash recorded"), +} + + +# ----------------------------------------------------------------------------- corpus +def seq(label, n): + x = zlib.crc32(label.encode()) or 1 + out = [] + for _ in range(n): + x = (x * 1103515245 + 12345) & 0x7FFFFFFF + out.append("ACGT"[(x >> 16) & 3]) + return "".join(out) + + +def rec(name, s, w=60, desc=""): + lines = [s] if w == 0 else [s[i:i + w] for i in range(0, len(s), w)] + return "\n".join([">" + name + (" " + desc if desc else "")] + lines) + "\n" + + +def put(s, i, ch): + return s[:i] + ch + s[i + 1:] + + +def build_corpus(): + """Return [(name, scope, expectation, rationale, bytes)]; order is the report order.""" + a, b = seq("a", 600), seq("b", 800) + base = rec("ctg1", a) + rec("ctg2", b) + iupac = a + for pos, ch in ((100, "R"), (150, "Y"), (200, "K"), (250, "M"), (300, "S"), (350, "W"), + (400, "N"), (450, "B"), (480, "D"), (510, "H"), (540, "V")): + iupac = put(iupac, pos, ch) + soft = a[:100] + a[100:300].lower() + a[300:] + chunks, i, k = [], 0, 0 + while i < 600: + w = (60, 80, 45, 70)[k % 4] + chunks.append(a[i:i + w]) + i, k = i + w, k + 1 + nn = "".join("N" if j % 5 in (1, 2, 3) else a[j] for j in range(600)) + c = [] + + def add(name, scope, expect, why, data): + c.append((name, scope, expect, why, data.encode() if isinstance(data, str) else data)) + + add("valid_simple", "control", "valid", "F: '>' SeqID line plus IUPAC lines; 2 records, 600/800 nt, wrap 60", base) + add("valid_single_line", "control", "valid", "F: sequence 'can contain returns', does not require them", rec("ctg1", a, 0) + rec("ctg2", b, 0)) + add("valid_softmasked", "control", "valid", "soft-masking is ubiquitous; F and G are silent on case, so no basis to reject", rec("ctg1", soft) + rec("ctg2", b)) + add("valid_iupac", "control", "valid", "F: 'only use IUPAC symbols'; R,Y,K,M,S,W,N,B,D,H,V all are, one each in 600 nt", rec("ctg1", iupac) + rec("ctg2", b)) + add("valid_no_final_newline", "control", "valid", "missing last newline is legal text; robustness check", base.rstrip("\n")) + add("empty_file", "syntax", "error", "F/G: a file is a set of '>' records; zero bytes contains no sequence", b"") + add("seq_before_header", "syntax", "error", "F: the definition line must precede the sequence", seq("pre", 60) + "\n" + base) + add("empty_header", "syntax", "error", "F: '>' must be followed by a SeqID; a nameless record cannot be referenced", rec("ctg1", a) + ">\n" + "\n".join(wrap_(b)) + "\n") + add("empty_record", "syntax", "error", "F: a definition line is followed by its sequence; zero residues defines nothing", rec("ctg1", a) + ">ctg2_empty\n" + rec("ctg3", b)) + add("dup_id_identical_header", "identifier", "error", "F: 'SeqID must be unique'; G: 'unique within a genome'", rec("ctg1", a, desc="isolate X") + rec("ctg1", b, desc="isolate X")) + add("dup_first_token_id", "identifier", "error", "F/G: SeqID is the first token; differing descriptions do not make it unique", rec("ctg1", a, desc="isolate A") + rec("ctg1", b, desc="isolate B")) + add("dup_sequence_diff_ids", "content", "advisory", "legal, but byte-identical records under two IDs usually signal a merge error", rec("ctg1", a) + rec("ctg2", a)) + add("digit_in_sequence", "syntax", "error", "F: 'only use IUPAC symbols'; a digit is not one", rec("ctg1", put(a, 300, "7"))) + add("stop_star_in_sequence", "syntax", "error", "F: '*' is a protein stop symbol, not an IUPAC nucleotide symbol", rec("ctg1", put(a, 300, "*"))) + add("gap_dash_in_sequence", "syntax", "advisory", "F: outside alignments '-' 'will be stripped'; not rejected, but silently altered", rec("ctg1", put(a, 300, "-"))) + add("non_iupac_letter_x", "syntax", "error", "F: 'only use IUPAC symbols'; X is not a nucleotide code", rec("ctg1", put(a, 300, "X"))) + add("space_in_sequence", "syntax", "advisory", "not IUPAC, but many parsers strip blanks, so reasonable tools differ", rec("ctg1", put(a, 30, " "))) + add("blank_line_in_record", "syntax", "advisory", "F/G silent; some parsers skip blanks, indexers (faidx) object", ">ctg1\n" + "\n".join(wrap_(a)[:2] + [""] + wrap_(a)[2:]) + "\n") + add("crlf_line_endings", "syntax", "advisory", "F/G silent; tolerated by most parsers, but stray CR leaks into IDs of some tools", base.replace("\n", "\r\n")) + add("inconsistent_wrapping", "syntax", "advisory", "F: returns inside sequence are allowed (<=80 recommended); only line-indexed tools object", ">ctg1\n" + "\n".join(chunks) + "\n") + add("utf8_bom", "syntax", "advisory", "BOM makes line 1 not start with '>' for byte-exact readers; common Windows artefact", b"\xef\xbb\xbf" + base.encode()) + add("high_n_record", "content", "advisory", "valid FASTA; 60% scattered N is a quality signal, not a format error", rec("ctg1", nn)) + add("all_n_record", "content", "error", "no residue information; also violates G 'remove any Ns from the beginning or end'", rec("ctg1", "N" * 600)) + add("tiny_contig_50", "content", "advisory", "G: 'Contigs should be >199nt' is a recommendation; dropping short contigs is a QC choice", rec("ctg1", a) + rec("tiny", seq("t", 50))) + add("seqid_len_49", "ncbi", "valid", "G: SeqIDs must be <50 characters (F says <=25 for general GenBank; G governs genomes)", rec("ctg_" + "x" * 45, a)) + add("seqid_len_50", "ncbi", "error", "G: SeqIDs must be <50 characters", rec("ctg_" + "x" * 46, a)) + add("seqid_disallowed_char", "ncbi", "error", "F/G: SeqID 'can only include' letters, digits, - _ . : * #; '@' is not allowed", rec("ctg@1", a)) + add("contig_len_199", "ncbi", "advisory", "G: 'Contigs should be >199nt' (wording is a recommendation, so not scored)", rec("ctg1", seq("c", 199))) + add("contig_len_200", "ncbi", "valid", "G: >199 nt satisfied exactly", rec("ctg1", seq("c", 200))) + add("terminal_ns", "ncbi", "error", "G: 'Remove any Ns from the beginning or end of each sequence'", rec("ctg1", "N" * 10 + a[10:590] + "N" * 10)) + return c + + +def wrap_(s, w=60): + return [s[i:i + w] for i in range(0, len(s), w)] + + +def sha(data): + return hashlib.sha256(data).hexdigest() + + +def corpus_sha(corpus): + return sha("".join("%s\t%s\n" % (n, sha(d)) for n, _, _, _, d in corpus).encode()) + + +# ----------------------------------------------------------------------------- runners +def sh(cmd, cwd, timeout, env=None): + try: + p = subprocess.run([str(x) for x in cmd], cwd=str(cwd), env=env, stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout, + text=True, errors="replace") + return p.returncode, p.stdout, p.stderr + except subprocess.TimeoutExpired: + return None, "", "timeout after %gs (process killed)" % timeout + except OSError as e: + return None, "", "cannot execute: %s" % e.strerror + + +def crashed(rc, err): + return rc is None or rc < 0 or "panic:" in err or "goroutine " in err or "Traceback" in err + + +def run_pyfv(exe, path, wd, to): + rc, out, err = sh([exe, "-f", path], wd, to, dict(os.environ, PYTHONWARNINGS="ignore")) + msg = err or out or "valid" + if crashed(rc, err) or rc not in (0, 1, 2, 4): + return "crash", "crash", rc, "crash or undocumented exit: " + msg + flag = "no" if rc == 0 else "yes" + return flag, flag, rc, msg + + +def run_falint(exe, path, wd, to): + rc, out, err = sh([exe, "-fasta", path], wd, to) + if crashed(rc, err): + return "crash", "crash", rc, err or out + m = re.search(r'msg="?([^"]*)"?', err) + flag = "yes" if rc != 0 or BAD_FALINT.search(err) else "no" + return flag, flag, rc, (m.group(1) if m else (err or out)) + + +def run_seqkit(exe, path, wd, to): + rc, out, err = sh([exe, "seq", "-v", "-t", "dna", path, "-o", os.devnull], wd, to) + err = ANSI.sub("", err) + if crashed(rc, err): + return "crash", "crash", rc, err + flag = "yes" if rc != 0 or "[ERRO]" in err or "[WARN]" in err else "no" + return flag, flag, rc, err or "no message" + + +def run_fg(exe, gate, path, wd, to): + wd.mkdir(parents=True, exist_ok=True) + for stale in ("fg.json", "fg.html", "fg.tsv", "fg_mqc.json"): + (wd / stale).unlink(missing_ok=True) # never parse a report left by an earlier run + cmd = [exe, path, "--profile", "assembly"] + gate + [ + "--json", wd / "fg.json", "--out", wd / "fg.html", "--tsv", wd / "fg.tsv", + "--multiqc", wd / "fg_mqc.json"] + rc, out, err = sh(cmd, wd, to) + if rc != 0: # FastaGuard exits 0 for PASS, WARN and FAIL once the reports are written + return "crash", "crash", rc, "fastaguard exit %s: %s" % (rc, (err or out).strip()) + try: + rep = json.loads((wd / "fg.json").read_text()) + g = rep["gate"] + verdict, blocked = rep["verdict"]["status"], not g["can_continue"] + except (OSError, ValueError, KeyError, TypeError): + return "crash", "crash", rc, "no usable JSON report: " + (err or out) + det = "verdict=%s gate=%s can_continue=%s blocking=%s advisory=%s" % ( + verdict, g["status"], str(g["can_continue"]).lower(), ",".join(g["blocking_findings"]) or "none", + ",".join(g["advisory_findings"]) or "none") + return ("yes" if verdict != "PASS" else "no"), ("yes" if blocked else "no"), rc, det + + +class Table2asn: + def __init__(self, exe, wd, to): + self.exe, self.wd, self.to, self.baseline = exe, wd, to, set() + self.template = wd / "table2asn-validation-template.sbt" + self.template.write_text(T2A_TEMPLATE) + + def parse_val(self, d, stem): + msgs = [] + val, stats = d / (stem + ".val"), d / (stem + ".stats") + if val.is_file(): + for line in val.read_text(errors="replace").splitlines(): + m = VAL_RE.match(line) + if m: + msgs.append((m.group(1).lower(), m.group(2), m.group(3))) + return msgs, True + if stats.is_file(): + txt = stats.read_text(errors="replace") + for n, lvl in re.findall(r"(\d+)\s+([A-Za-z]+)-level", txt): + if int(n) and lvl.lower() in ERR_LEVELS: + msgs.append((lvl.lower(), "stats", "%s %s-level message(s)" % (n, lvl))) + for code in re.findall(r"^(\w+\.\w+):\s+\d+", txt, re.M): + msgs.append(("warning", code, "(from .stats)")) + return msgs, True + return msgs, False + + def __call__(self, path, wd): + d = self.wd / "table2asn" / wd.name + d.mkdir(parents=True, exist_ok=True) + name = path.name[:-3] if path.name.endswith(".gz") else path.name + inp = d / name + if path.name.endswith(".gz"): + import gzip + with gzip.open(path, "rb") as src, open(inp, "wb") as dst: + shutil.copyfileobj(src, dst) + else: + shutil.copyfile(path, inp) + rc, out, err = sh([self.exe, "-t", self.template, "-i", inp] + T2A_ARGS, d, self.to) + console = ANSI.sub("", out + "\n" + err) + if crashed(rc, err): + return "crash", "crash", rc, err + msgs, have = self.parse_val(d, inp.stem if inp.suffix else inp.name) + errs = [m for m in msgs if m[0] in ERR_LEVELS] + cerr = ERR_RE.search(console) is not None + problem = " ".join(re.findall(r"Problem:\s*(.*)", console)) + if cerr and "Cannot open" in problem: + return "crash", "crash", rc, "console: " + problem + if cerr: + return "yes", "yes", rc, "console Error: " + (problem or console) + if errs: + return "yes", "yes", rc, "; ".join("%s [%s] %s" % (m[0], m[1], m[2][:70]) for m in errs[:3]) + cwarn = problem if re.search(r"(?i)(?:^|[\s\[])WARNING\s*:", console) else "" + new = [m for m in msgs if m[0].startswith("warn") and m[1] not in self.baseline] + if (rc != 0 or not have) and not (new or cwarn): + return "crash", "crash", rc, "no error evidence for non-zero exit / no artifact: " + console + if new or cwarn: + det = ["warning [%s] %s" % (m[1], m[2][:70]) for m in new[:3]] + return "yes", "no", rc, "; ".join(det + (["console Warning: " + cwarn] if cwarn else [])) + return "no", "no", rc, "only baseline warnings (%d)" % len(msgs) + + def set_baseline(self, path, wd): + self(path, wd) + d = self.wd / "table2asn" / wd.name + msgs, _ = self.parse_val(d, path.stem) + self.baseline = {m[1] for m in msgs if m[0].startswith("warn")} + + +# ----------------------------------------------------------------------------- reporting +def cell(row): + f = row["flagged"] + return ("yes" if row["strict"] == "yes" else "warn") if f == "yes" else f + + +def tally(rows, tool, key, scope): + t = {} + for e in ("error", "valid", "advisory"): + t.update({e + "_flagged": 0, e + "_total": 0, e + "_crash": 0}) + for r in rows: + if r["tool"] != tool or r[key] == "n.a." or r["expectation"] not in ("error", "valid", "advisory"): + continue + if scope != "all" and r["scope"] != scope: + continue + t[r["expectation"] + "_total"] += 1 + t[r["expectation"] + "_flagged"] += r[key] == "yes" + t[r["expectation"] + "_crash"] += r[key] == "crash" + return t + + +def clean(v, maps=()): + s = ANSI.sub("", str(v)) + for a, b in maps: + s = s.replace(a, b) + return " ".join(s.split())[:260].strip() or "-" + + +PREAMBLE = [ + "# NOTE: the corpus is author-constructed and favours FastaGuard's own check list.", + "# Expectations (valid/error/advisory) were fixed before any tool ran; advisory rows are not scored.", + "# The tools were not designed for the same purpose. flagged: yes|no|crash|n.a.;" + " strict: blocked / error-level.", +] + + +def write_tsv(path, header, rows, preamble=()): + lines = list(preamble) + ["\t".join(header)] + lines += ["\t".join(clean(x) for x in r) for r in rows] + path.write_text("\n".join(lines) + "\n") + + +def summary(rows, real_rows, tools_run): + out = ["# Author-constructed corpus favouring FastaGuard's check list; tools differ in purpose.", + "# block: scores (flagged = any report; strict = FastaGuard blocked / table2asn error-level)", + "\t".join(["tool", "definition", "scope", "error_flagged", "error_total", "error_crash", + "valid_flagged", "valid_total", "valid_crash", "advisory_flagged", + "advisory_total", "advisory_crash"])] + for tool in tools_run: + for key in ("flagged", "strict") if tool in TWO_LEVEL else ("flagged",): + for scope in ("all", "control", "syntax", "identifier", "content", "ncbi"): + t = tally(rows, tool, key, scope) + out.append("\t".join([tool, key, scope] + [str(t[k + s]) for k in ("error", "valid", "advisory") + for s in ("_flagged", "_total", "_crash")])) + out += ["# block: capability_matrix (yes = flagged and blocked/error-level or single-level tool;" + " warn = flagged below block/error level; no; crash; n.a.)", + "\t".join(["file", "scope", "expectation"] + TOOLS)] + for key in dict.fromkeys(r["file"] for r in rows): + by = {r["tool"]: r for r in rows if r["file"] == key} + first = by[TOOLS[0]] + out.append("\t".join([key, first["scope"], first["expectation"]] + [cell(by[t]) for t in TOOLS])) + if real_rows: + out += ["# block: real_files (no expectation; see real_results.tsv for detail)", + "\t".join(["file"] + TOOLS)] + for key in dict.fromkeys(r["file"] for r in real_rows): + by = {r["tool"]: r for r in real_rows if r["file"] == key} + out.append("\t".join([key] + [cell(by[t]) for t in TOOLS])) + return "\n".join(out) + "\n" + + +def tool_version(exe, args, maps): + rc, out, err = sh([exe] + args, exe.parent, 30, dict(os.environ, PYTHONWARNINGS="ignore")) + text = ANSI.sub("", (out or err).strip().splitlines()[0] if (out or err).strip() else "unknown") + m = re.search(r"version\s+(\S+)", text) + return clean(m.group(1) if m else text, maps) + + +def provenance(paths, archives, maps): + found = {} + if archives: + for p in Path(archives).rglob("*"): + if p.is_file(): + found.setdefault(p.name, []).append(p) + entries = [] + for tool, (url, digest, vargs, note) in SOURCES.items(): + exe = paths.get(tool) + verified = None + if digest: + hits = found.get(url.rsplit("/", 1)[-1], []) + verified = (sha(hits[0].read_bytes()) == digest) if hits else None + entries.append({ + "tool": tool, "version": tool_version(exe, vargs, maps) if exe else "not installed", + "source_url": url, "archive_sha256_pinned": digest, "archive_sha256_verified": verified, + "executable_sha256": sha(exe.read_bytes()) if exe else None, "notes": note}) + return entries + + +def resolve(flag, env, name): + cand = flag or os.environ.get(env) or shutil.which(name) + p = Path(cand).expanduser() if cand else None + return p.resolve() if p and p.is_file() and os.access(p, os.X_OK) else None + + +def main(): + ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + ap.add_argument("--corpus-dir", type=Path, required=True) + ap.add_argument("--out-dir", type=Path, default=Path(__file__).resolve().parent) + ap.add_argument("--work-dir", type=Path) + ap.add_argument("--timeout", type=float, default=60.0) + ap.add_argument("--archives", type=Path) + ap.add_argument("--generate-only", action="store_true") + ap.add_argument("--real", nargs="+", default=[], metavar="[LABEL=]FASTA") + ap.add_argument("--real-timeout", type=float, default=600.0) + for flag in ("fastaguard", "py-fasta-validator", "fa-lint", "table2asn", "seqkit"): + ap.add_argument("--" + flag, type=Path) + args = ap.parse_args() + + corpus = build_corpus() + cdir = args.corpus_dir.resolve() + cdir.mkdir(parents=True, exist_ok=True) + for name, _, _, _, data in corpus: + (cdir / (name + ".fa")).write_bytes(data) + if args.generate_only: + print("# corpus_sha256\t" + corpus_sha(corpus)) + for name, scope, expect, why, data in corpus: + print("\t".join([name, scope, expect, sha(data)[:16], why])) + return 0 + + paths = {"py_fasta_validator": resolve(args.py_fasta_validator, "PY_FASTA_VALIDATOR", "py_fasta_validator"), + "fa-lint": resolve(args.fa_lint, "FA_LINT", "fa-lint"), + "table2asn": resolve(args.table2asn, "TABLE2ASN", "table2asn"), + "seqkit": resolve(args.seqkit, "SEQKIT", "seqkit"), + "fastaguard": resolve(args.fastaguard, "FASTAGUARD", "fastaguard")} + tmp = None if args.work_dir else tempfile.mkdtemp(prefix="fg-compare-") + work = (args.work_dir or Path(tmp)).resolve() + work.mkdir(parents=True, exist_ok=True) + maps = [(str(work), "$WORK"), (str(cdir), "$CORPUS")] + [ + (str(p.parent), "$TOOLDIR") for p in paths.values() if p] + to = [args.timeout] # one-element list so the real-file phase can raise it + t2a = Table2asn(paths["table2asn"], work, to[0]) if paths["table2asn"] else None + runners = { + "py_fasta_validator": paths["py_fasta_validator"] and (lambda p, w: run_pyfv(paths["py_fasta_validator"], p, w, to[0])), + "fa-lint": paths["fa-lint"] and (lambda p, w: run_falint(paths["fa-lint"], p, w, to[0])), + "table2asn": t2a, + "seqkit": paths["seqkit"] and (lambda p, w: run_seqkit(paths["seqkit"], p, w, to[0])), + "fastaguard_pipeline": paths["fastaguard"] and (lambda p, w: run_fg( + paths["fastaguard"], ["--gate", "pipeline"], p, w / "fgp", to[0])), + "fastaguard_ncbi": paths["fastaguard"] and (lambda p, w: run_fg( + paths["fastaguard"], ["--gate", "submission", "--submission-target", "ncbi"], p, w / "fgn", to[0])), + } + if t2a: + wd = work / "case" / "valid_simple" + wd.mkdir(parents=True, exist_ok=True) + t2a.set_baseline(cdir / "valid_simple.fa", wd) + + def run_all(label, scope, expect, path, extra=()): + wd = work / "case" / label + wd.mkdir(parents=True, exist_ok=True) + res = [] + for tool in TOOLS: + fn = runners[tool] + f, s, rc, det = fn(path, wd) if fn else ("n.a.", "n.a.", None, "tool not found") + res.append({"file": label, "scope": scope, "expectation": expect, "tool": tool, + "flagged": f, "strict": s, "exit_code": "-" if rc is None else rc, + "detail": clean(det, maps + list(extra))}) + return res + + rows = [] + for name, scope, expect, _, _ in corpus: + rows += run_all(name, scope, expect, cdir / (name + ".fa")) + real_rows = [] + to[0] = args.real_timeout + if t2a: + t2a.to = to[0] + seen = set() + for spec in args.real: + label, sep, rest = spec.partition("=") + p = Path(rest if sep else spec) + label = label if sep else p.name + if label in seen: + sys.exit("duplicate --real label %r: use LABEL=PATH to make labels unique" % label) + seen.add(label) + real_rows += run_all(label, "real", "n/a", p.resolve(), [(str(p.resolve().parent), "$REAL")]) + + cols = ["file", "scope", "expectation", "tool", "flagged", "strict", "exit_code", "detail"] + out = args.out_dir + out.mkdir(parents=True, exist_ok=True) + write_tsv(out / "results.tsv", cols, [[r[c] for c in cols] for r in rows], PREAMBLE) + if real_rows: + write_tsv(out / "real_results.tsv", cols, [[r[c] for c in cols] for r in real_rows], + ["# Real files: no expectation, nothing scored. Same decision rules as results.tsv."]) + tools_run = [t for t in TOOLS if runners[t]] + (out / "summary.tsv").write_text(summary(rows, real_rows, tools_run)) + info = {"note": "Author-constructed corpus favouring FastaGuard's check list; tools differ in purpose.", + "corpus_sha256": corpus_sha(corpus), "corpus_files": len(corpus), + "platform": "%s %s" % (platform.system(), platform.machine()), + "tools": provenance(paths, args.archives, maps)} + (out / "tool_versions.json").write_text(json.dumps(info, indent=2, sort_keys=True) + "\n") + if tmp: + shutil.rmtree(tmp, ignore_errors=True) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/paper/comparison/summary.tsv b/paper/comparison/summary.tsv new file mode 100644 index 0000000..4cd9138 --- /dev/null +++ b/paper/comparison/summary.tsv @@ -0,0 +1,93 @@ +# Author-constructed corpus favouring FastaGuard's check list; tools differ in purpose. +# block: scores (flagged = any report; strict = FastaGuard blocked / table2asn error-level) +tool definition scope error_flagged error_total error_crash valid_flagged valid_total valid_crash advisory_flagged advisory_total advisory_crash +py_fasta_validator flagged all 5 13 0 0 7 0 3 10 0 +py_fasta_validator flagged control 0 0 0 0 5 0 0 0 0 +py_fasta_validator flagged syntax 3 7 0 0 0 0 3 6 0 +py_fasta_validator flagged identifier 2 2 0 0 0 0 0 0 0 +py_fasta_validator flagged content 0 1 0 0 0 0 0 3 0 +py_fasta_validator flagged ncbi 0 3 0 0 2 0 0 1 0 +fa-lint flagged all 9 13 0 0 7 0 5 10 0 +fa-lint flagged control 0 0 0 0 5 0 0 0 0 +fa-lint flagged syntax 6 7 0 0 0 0 5 6 0 +fa-lint flagged identifier 2 2 0 0 0 0 0 0 0 +fa-lint flagged content 1 1 0 0 0 0 0 3 0 +fa-lint flagged ncbi 0 3 0 0 2 0 0 1 0 +table2asn flagged all 7 13 3 0 7 0 3 10 0 +table2asn flagged control 0 0 0 0 5 0 0 0 0 +table2asn flagged syntax 5 7 1 0 0 0 2 6 0 +table2asn flagged identifier 0 2 2 0 0 0 0 0 0 +table2asn flagged content 1 1 0 0 0 0 1 3 0 +table2asn flagged ncbi 1 3 0 0 2 0 0 1 0 +table2asn strict all 7 13 3 0 7 0 2 10 0 +table2asn strict control 0 0 0 0 5 0 0 0 0 +table2asn strict syntax 5 7 1 0 0 0 1 6 0 +table2asn strict identifier 0 2 2 0 0 0 0 0 0 +table2asn strict content 1 1 0 0 0 0 1 3 0 +table2asn strict ncbi 1 3 0 0 2 0 0 1 0 +seqkit flagged all 5 13 0 0 7 0 0 10 0 +seqkit flagged control 0 0 0 0 5 0 0 0 0 +seqkit flagged syntax 5 7 0 0 0 0 0 6 0 +seqkit flagged identifier 0 2 0 0 0 0 0 0 0 +seqkit flagged content 0 1 0 0 0 0 0 3 0 +seqkit flagged ncbi 0 3 0 0 2 0 0 1 0 +fastaguard_pipeline flagged all 11 13 0 0 7 0 7 10 0 +fastaguard_pipeline flagged control 0 0 0 0 5 0 0 0 0 +fastaguard_pipeline flagged syntax 7 7 0 0 0 0 3 6 0 +fastaguard_pipeline flagged identifier 2 2 0 0 0 0 0 0 0 +fastaguard_pipeline flagged content 1 1 0 0 0 0 3 3 0 +fastaguard_pipeline flagged ncbi 1 3 0 0 2 0 1 1 0 +fastaguard_pipeline strict all 10 13 0 0 7 0 4 10 0 +fastaguard_pipeline strict control 0 0 0 0 5 0 0 0 0 +fastaguard_pipeline strict syntax 7 7 0 0 0 0 3 6 0 +fastaguard_pipeline strict identifier 2 2 0 0 0 0 0 0 0 +fastaguard_pipeline strict content 1 1 0 0 0 0 1 3 0 +fastaguard_pipeline strict ncbi 0 3 0 0 2 0 0 1 0 +fastaguard_ncbi flagged all 13 13 0 0 7 0 7 10 0 +fastaguard_ncbi flagged control 0 0 0 0 5 0 0 0 0 +fastaguard_ncbi flagged syntax 7 7 0 0 0 0 3 6 0 +fastaguard_ncbi flagged identifier 2 2 0 0 0 0 0 0 0 +fastaguard_ncbi flagged content 1 1 0 0 0 0 3 3 0 +fastaguard_ncbi flagged ncbi 3 3 0 0 2 0 1 1 0 +fastaguard_ncbi strict all 13 13 0 0 7 0 5 10 0 +fastaguard_ncbi strict control 0 0 0 0 5 0 0 0 0 +fastaguard_ncbi strict syntax 7 7 0 0 0 0 3 6 0 +fastaguard_ncbi strict identifier 2 2 0 0 0 0 0 0 0 +fastaguard_ncbi strict content 1 1 0 0 0 0 1 3 0 +fastaguard_ncbi strict ncbi 3 3 0 0 2 0 1 1 0 +# block: capability_matrix (yes = flagged and blocked/error-level or single-level tool; warn = flagged below block/error level; no; crash; n.a.) +file scope expectation py_fasta_validator fa-lint table2asn seqkit fastaguard_pipeline fastaguard_ncbi +valid_simple control valid no no no no no no +valid_single_line control valid no no no no no no +valid_softmasked control valid no no no no no no +valid_iupac control valid no no no no no no +valid_no_final_newline control valid no no no no no no +empty_file syntax error no yes crash no yes yes +seq_before_header syntax error yes yes yes yes yes yes +empty_header syntax error no yes no yes yes yes +empty_record syntax error no yes yes no yes yes +dup_id_identical_header identifier error yes yes crash no yes yes +dup_first_token_id identifier error yes yes crash no yes yes +dup_sequence_diff_ids content advisory no no no no warn warn +digit_in_sequence syntax error yes yes yes yes yes yes +stop_star_in_sequence syntax error yes yes yes yes yes yes +gap_dash_in_sequence syntax advisory yes yes warn no yes yes +non_iupac_letter_x syntax error no no yes yes yes yes +space_in_sequence syntax advisory yes yes no no yes yes +blank_line_in_record syntax advisory no yes no no no no +crlf_line_endings syntax advisory no no no no no no +inconsistent_wrapping syntax advisory no yes no no no no +utf8_bom syntax advisory yes yes yes no yes yes +high_n_record content advisory no no yes no yes warn +all_n_record content error no yes yes no yes yes +tiny_contig_50 content advisory no no no no warn yes +seqid_len_49 ncbi valid no no no no no no +seqid_len_50 ncbi error no no no no no yes +seqid_disallowed_char ncbi error no no no no no yes +contig_len_199 ncbi advisory no no no no warn yes +contig_len_200 ncbi valid no no no no no no +terminal_ns ncbi error no no yes no warn yes +# block: real_files (no expectation; see real_results.tsv for detail) +file py_fasta_validator fa-lint table2asn seqkit fastaguard_pipeline fastaguard_ncbi +ecoli_GCF_000005845.2 no no no no no no +neurospora_GCF_000182925.2 no no yes no warn warn diff --git a/paper/comparison/tool_versions.json b/paper/comparison/tool_versions.json new file mode 100644 index 0000000..bbe461c --- /dev/null +++ b/paper/comparison/tool_versions.json @@ -0,0 +1,53 @@ +{ + "corpus_files": 30, + "corpus_sha256": "0d8aa8f02cbac6164750ad719b1bd36c973f55bd924ee733478e9b67934f9181", + "note": "Author-constructed corpus favouring FastaGuard's check list; tools differ in purpose.", + "platform": "Darwin arm64", + "tools": [ + { + "archive_sha256_pinned": "ca29c20e1be31ef190f923b62ddb7fccda8c1c3ba9cf699a6ccaccce10ea011a", + "archive_sha256_verified": true, + "executable_sha256": "3360f4602a710d3ce7d239fe89894c94a9cd07d8ed10612786c3cf0d9dccbeb7", + "notes": "PyPI sdist of project py-fasta-validator; built in a python3.11 venv with setuptools==80.9.0 because the shipped CLI imports pkg_resources", + "source_url": "https://files.pythonhosted.org/packages/source/p/py_fasta_validator/py_fasta_validator-0.6.tar.gz", + "tool": "py_fasta_validator", + "version": "0.6" + }, + { + "archive_sha256_pinned": "6edcb792489b18bcfd03156912d05227ac05cdf657c517041c40489566470436", + "archive_sha256_verified": true, + "executable_sha256": "5330aa5003cee754d26951c53f251a7546fe2006962cfeb1a281353fc079026d", + "notes": "official release binary", + "source_url": "https://github.com/GallVp/fa-lint/releases/download/1.2.0/fa-lint-darwin-arm64.tar.gz", + "tool": "fa-lint", + "version": "1.2.0" + }, + { + "archive_sha256_pinned": "348b9d5f1b05065f4e5e61b6c2350e5ff044de2d6dc14765be6ac02c00e59341", + "archive_sha256_verified": true, + "executable_sha256": "94f276a1283be339f10272f71963bce22053a8463987bac3c1ed55a7078583c1", + "notes": "digest equals the one pinned in docs/evidence/fastaguard-v0.7-ncbi-genome-policy.md; NCBI publishes no separate digest", + "source_url": "https://ftp.ncbi.nlm.nih.gov/asn1-converters/by_program/table2asn/mac.table2asn.gz", + "tool": "table2asn", + "version": "table2asn: 1.29.324" + }, + { + "archive_sha256_pinned": "7b3a6a7c9be07a06c5b308d1aad769c09762b36b387690eed8b91b00d01503b7", + "archive_sha256_verified": true, + "executable_sha256": "4939e4d15cca77bdfff5d1904447b7721ce93d2026462e7d302de95e6f664537", + "notes": "official release binary; md5 2b335eb27f69af97a252818a85145684 also matched", + "source_url": "https://github.com/shenwei356/seqkit/releases/download/v2.14.0/seqkit_darwin_arm64.tar.gz", + "tool": "seqkit", + "version": "seqkit v2.14.0" + }, + { + "archive_sha256_pinned": null, + "archive_sha256_verified": null, + "executable_sha256": "8a76785d89f2f7c7f569dba0bbc741124f8b506851f5bff25d5eb134d87b897f", + "notes": "no archive; executable hash recorded", + "source_url": "local build of `git archive v1.0.0` of https://github.com/ehsanestaji/FastaGuard", + "tool": "fastaguard", + "version": "fastaguard 1.0.0" + } + ] +} diff --git a/paper/cover-letter.md b/paper/cover-letter.md index 1875f68..576da67 100644 --- a/paper/cover-letter.md +++ b/paper/cover-letter.md @@ -39,12 +39,14 @@ available at the stable URL above, and archived on Zenodo **Preprint disclosure.** The same manuscript has been submitted to bioRxiv as a preprint. We will update the preprint with the published DOI after acceptance. -**AI use disclosure.** AI coding assistants (including Cursor-based agents and -related large-language-model tools) were used for planning, manuscript -scaffolding, repository evidence cross-checks, and prose drafting support. The -authors reviewed all output and remain responsible for the manuscript, code, -evidence interpretation, and all scientific claims. The same statement appears in -the manuscript. +**AI use disclosure.** AI coding assistants (Cursor-based agents and Anthropic +Claude Code) were used for planning, manuscript scaffolding, repository evidence +cross-checks, prose drafting support, and revision. The software, code base, and +benchmarking results were completed first; the manuscript was then drafted solely +from that code base and those results, not from a free-form prompt. The authors +reviewed all output and remain responsible for the manuscript, code, evidence +interpretation, and all scientific claims. The same statement appears in the +manuscript. Both corresponding authors can be reached at ehsan.estaji@umu.se and jianfeng.mao@umu.se. We confirm that the work is original, is not under diff --git a/paper/main.tex b/paper/main.tex index 4db8109..530e8aa 100644 --- a/paper/main.tex +++ b/paper/main.tex @@ -85,7 +85,7 @@ \noindent\textbf{Supplementary information:} Finding-catalog excerpts, evidence packs, and reproducibility notes are maintained -with the software repository under \texttt{docs/}. +with the software repository under \texttt{docs/} and \texttt{paper/comparison/}. \end{abstract} \vspace{0.4em} @@ -201,7 +201,7 @@ \section{Implementation} conservative \texttt{ncbi\_genome} policy snapshot (first-token SeqID syntax and length limits, minimum record length, terminal Ns) based on NCBI submission and FASTA guidance~\citep{ncbi_genome_submit,ncbi_fasta_format}; in a local -differential check it was stricter than table2asn on several boundary cases +differential check it was stricter than table2asn~\citep{ncbi_table2asn} on several boundary cases (for example, a 199-bp record or a 50-character SeqID), and it does not replace NCBI FCS~\citep{astashyn2024fcsgx}, annotation validation, or repository acceptance decisions. @@ -292,9 +292,10 @@ \section{Results} (duplicate identifiers, invalid characters, high-N content) while still exiting~0 after writing a complete FAIL report---matching the v0.6 workflow contract that separates QC status from process success. -Re-running the two local fixtures with the v1.0.0 binary reproduced the same -verdicts, gate statuses, input SHA-256 digests, and exit status; the two NCBI -assemblies were not re-run. +Re-running all five evidence-pack inputs with a build of the v1.0.0 tag reproduced +the same verdicts, gate statuses, blocking findings, lengths, sequence counts, +input SHA-256 digests, and exit status; timings differed between runs and are +indicative only. These examples are contract demonstrations, not a representative biological benchmark across taxa or assemblers. @@ -331,6 +332,60 @@ \section{Results} \end{tabular} \end{table} +To place these behaviours against existing tools, we ran +py\_fasta\_validator~0.6, fa-lint~1.2.0, table2asn~1.29.324~\citep{ncbi_table2asn}, +SeqKit~2.14.0, and FastaGuard v1.0.0 on 30 small constructed FASTA files +(7 valid, 13 defective, and 10 advisory files that are not scored; +Table~\ref{tab:comparison}), with expectations written down before any tool was +run. +No tool flagged a valid file. +FastaGuard's NCBI gate flagged all 13 defective files and its pipeline gate 11 +(blocking 10); fa-lint flagged 9, table2asn 7, and py\_fasta\_validator and SeqKit +5 each. +table2asn returned no validation result on three inputs: an empty file, which it +could not open, and two duplicate-identifier files, on which it did not terminate +within the 30~s limit. +Only FastaGuard's NCBI gate flagged a 50-character SeqID and a SeqID containing +\texttt{@}; table2asn accepted both. +FastaGuard did not flag blank lines inside records or uneven line wrapping, which +fa-lint does. +On the two RefSeq assemblies, no tool flagged \emph{E.\ coli}; in \emph{N.\ crassa}, +py\_fasta\_validator, fa-lint, and SeqKit flagged nothing, whereas table2asn +reported error-level internal runs of 100~Ns (\texttt{InternalNsInSeqRaw}), which +FastaGuard reports as non-blocking gap-pattern advisories (WARN). +The corpus was built by the FastaGuard authors, its NCBI-rule rows encode the +policy that FastaGuard's NCBI gate implements, and the tools serve different +purposes, so Table~\ref{tab:comparison} compares capabilities, not accuracy on +real submissions. + +\begin{table}[t] + \centering + \caption{Capability comparison on 30 author-constructed FASTA files (13 + defective and 7 valid; 10 advisory files are not scored). Entries are defective + files flagged, out of the number shown; no valid file was flagged by any tool. + pyFV, py\_fasta\_validator; t2a, table2asn; FG, FastaGuard v1.0.0 with the + pipeline or NCBI submission gate (FG\textsubscript{pipe} blocked 10 of its 11). + The corpus favours FastaGuard's check list and the tools differ in purpose: this + is not a performance or accuracy benchmark. + $^{a}$No validation result on three inputs (an empty file and two + duplicate-identifier files).} + \label{tab:comparison} + \small + \begin{tabular}{@{}lcccccc@{}} + \toprule + Defect class (files) & pyFV & fa-lint & t2a & SeqKit + & FG\textsubscript{pipe} & FG\textsubscript{NCBI} \\ + \midrule + Syntax (7) & 3 & 6 & 5$^{a}$ & 5 & 7 & 7 \\ + Identifiers (2) & 2 & 2 & 0$^{a}$ & 0 & 2 & 2 \\ + All-N record (1) & 0 & 1 & 1 & 0 & 1 & 1 \\ + NCBI rules (3) & 0 & 0 & 1 & 0 & 1 & 3 \\ + \midrule + All defective (13) & 5 & 9 & 7$^{a}$ & 5 & 11 & 13 \\ + \bottomrule + \end{tabular} +\end{table} + A minimal workflow pattern is therefore: run FastaGuard with \texttt{--gate pipeline} and an output bundle; inspect \texttt{gate.can\_continue} in JSON (for example with \texttt{jq}); only then @@ -354,7 +409,7 @@ \section{Discussion} Relative to SeqKit and SeqFu, FastaGuard trades general sequence manipulation for an opinionated QC verdict and gate policy; relative to syntax validators such as py\_fasta\_validator and fa-lint, it adds severity-graded, explainable -findings and a structured report. +findings and a structured report (Table~\ref{tab:comparison}). Relative to QUAST, BUSCO, BlobToolKit, and CheckM, it runs earlier and answers a different question: whether the FASTA is safe to spend those tools on. Relative to MultiQC, it is a producer of aggregatable content rather than an @@ -368,8 +423,8 @@ \section{Discussion} Public-evidence timings are contextual to one machine and release line; authors should re-measure on their targets before citing absolute performance. Finding thresholds are heuristics: we did not measure detection or -false-positive rates, nor compare FastaGuard with other validators on a common -defect corpus. +false-positive rates on real submissions, and the validator comparison +(Table~\ref{tab:comparison}) uses a small author-constructed corpus. The behaviour described here corresponds to release v1.0.0; later changes are recorded in the project changelog. @@ -387,9 +442,12 @@ \section*{Conflict of interest} The authors declare no competing interests. \section*{AI usage disclosure} -AI coding assistants (including Cursor-based agents and related large-language-model -tools) were used for planning, manuscript scaffolding, repository evidence -cross-checks, and prose drafting support. +AI coding assistants (Cursor-based agents and Anthropic Claude Code) were used for +planning, manuscript scaffolding, repository evidence cross-checks, prose drafting +support, and revision. +The software, code base, and benchmarking results were completed first; the +manuscript was then drafted solely from that code base and those results, not from +a free-form prompt. The authors reviewed all output and remain responsible for the manuscript, code, evidence interpretation, and all scientific claims. @@ -402,11 +460,13 @@ \section*{Data availability} available at \url{https://github.com/ehsanestaji/FastaGuard} under the MIT license (cited release tag \texttt{v1.0.0}). Committed public-evidence summaries used in Table~\ref{tab:evidence} are -archived under \texttt{docs/evidence/} (v0.6.0 portable JSON/TSV pack) in the -repository; the rows can be regenerated with +archived under \texttt{docs/evidence/} (v0.6.0 portable JSON/TSV pack, plus the +v1.0.0 re-run summary) in the repository; the rows can be regenerated with \texttt{scripts/collect\_evidence.py}, which retrieves the NCBI assemblies by accession with the NCBI Datasets command-line tool and records input SHA-256 digests in the TSV. +The validator-comparison script, per-file results, and tool versions with SHA-256 +checksums are in \texttt{paper/comparison/}. No new biological samples were generated for this study; public NCBI assemblies cited in the evidence pack are used under their existing data-access terms. diff --git a/paper/references.bib b/paper/references.bib index 1df79e4..7f2f79b 100644 --- a/paper/references.bib +++ b/paper/references.bib @@ -247,3 +247,11 @@ @article{galagan2003neurospora pages = {859--868}, doi = {10.1038/nature01554} } + +@misc{ncbi_table2asn, + author = {{National Center for Biotechnology Information}}, + title = {What is table2asn?}, + year = {2026}, + howpublished = {\url{https://www.ncbi.nlm.nih.gov/genbank/table2asn/}}, + note = {Version 1.29.324; accessed 2026-10-24} +} diff --git a/paper/submission.yml b/paper/submission.yml index f70a379..46f3645 100644 --- a/paper/submission.yml +++ b/paper/submission.yml @@ -10,7 +10,7 @@ oup_template_deferred_to: revision # Machine-check status after `make check` (human ScholarOne/bioRxiv steps remain USER-ACTION). submission_status: ready_for_author_upload -author_gate_status: pending_coauthor_confirmation +author_gate_status: confirmed # 2026-10-24, reported in chat by Ehsan Estaji (Jian-Feng Mao knew of the work and agreed); no message from Jian-Feng seen by the agent zenodo_gate_status: deposited biorxiv_status: not_submitted scholarone_status: not_submitted @@ -26,6 +26,8 @@ manuscript: biorxiv_pdf: paper/build/fastaguard-biorxiv.pdf title: "FastaGuard: FASTA preflight QC for modern bioinformatics pipelines" cover_letter: paper/cover-letter.md + validator_comparison: paper/comparison/ + evidence_rerun_v1_0_0: docs/evidence/results/v1.0.0-rerun/ authors: - name: Ehsan Estaji @@ -108,7 +110,6 @@ required_human_inputs: blockers_for_machine_ready: [] blockers_for_human_submit: - - Co-author final confirmation (also required by bioRxiv: all authors must consent to deposit) - bioRxiv deposit (login by Ehsan; affiliation required) - ScholarOne upload (credentials) - APC / institutional Read & Publish / ISCB discount check From 768ca220469dde5c0b74ef7285c81a20998c7a21 Mon Sep 17 00:00:00 2001 From: Ehsan ESTAJI <71376358+ehsanestaji@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:46:50 +0200 Subject: [PATCH 9/9] docs(paper): record the bioRxiv submission BIORXIV/2026/761563 in the manifest and handoff The preprint was submitted on 2026-10-24 and bioRxiv acknowledged it; it is in screening and has no DOI yet. Update biorxiv_status, the human-blocker list, the handoff status table (with the form notes learned during submission) and the readiness file so a resuming agent starts from the real state. Co-Authored-By: Claude Sonnet 5.5 --- docs/paper-readiness.md | 2 +- paper/HANDOFF.md | 12 +++++++++++- paper/submission.yml | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/paper-readiness.md b/docs/paper-readiness.md index e2c986c..7f2a590 100644 --- a/docs/paper-readiness.md +++ b/docs/paper-readiness.md @@ -38,7 +38,7 @@ Machine check: `cd paper && make check`. | --- | --- | | Co-author final OK (Jian-Feng Mao) | DONE 2026-10-24: reported in chat by Ehsan Estaji (Jian-Feng agreed and knew of the work); no written message seen by the agent | | Merge PR #21 | `paper/comparison/` is not in the v1.0.0 archive; merge before submission so paths in the paper resolve | -| bioRxiv deposit | Follow [`paper/biorxiv-checklist.md`](../paper/biorxiv-checklist.md); all authors must consent; cover letter discloses it | +| Record bioRxiv DOI | Submitted 2026-10-24 as `BIORXIV/2026/761563`; after screening (24-72 h) add the DOI to `paper/submission.yml` and the cover letter | | ScholarOne upload | Format-free first submission OK; upload PDF + cover letter; OUP General Template deferred to **revision** | | APC / Read & Publish / ISCB discount | Check before acceptance | | Suggested editors (optional) | Cover letter suggests assembly/sequence-analysis tools area | diff --git a/paper/HANDOFF.md b/paper/HANDOFF.md index 19a48da..46e1bd2 100644 --- a/paper/HANDOFF.md +++ b/paper/HANDOFF.md @@ -10,7 +10,8 @@ PR: [#21](https://github.com/ehsanestaji/FastaGuard/pull/21) · branch `cursor/p | --- | --- | | **DONE / submit-package ready** | Full draft; front-matter; evidence freeze; `submission.yml`; `check_submission.py`; cover letter; `.zenodo.json`; bioRxiv build target; `docs/paper-readiness.md` | | **DONE (2026-10-24)** | Zenodo DOI minted and pasted into Availability + manifest; OUP guideline review (AI + preprint disclosure added to cover letter); external claims verified (see below); PDF metadata + Fig. 1 label overlap fixed | -| **USER-ACTION only** | bioRxiv deposit; ScholarOne upload; APC/R&P/ISCB | +| **DONE (2026-10-24)** | bioRxiv preprint submitted as `BIORXIV/2026/761563` (acknowledgement email received); in screening, DOI pending | +| **USER-ACTION only** | Record the bioRxiv DOI when issued; ScholarOne upload; APC/R&P/ISCB; merge PR #21 | Do **not** claim bioRxiv or ScholarOne submitted from this agent track until the confirmation page / DOI is seen. Never click final Submit without explicit user OK. @@ -94,6 +95,15 @@ Zenodo archive; consider a small separate deposit for test data); `CITATION.cff` description still says Bioconda "remains at v0.7.0"; length (Introduction-Discussion ~2,260 words; ~2,700 incl. abstract, captions and declarations; ~850 more in references; draft PDF is 8 pages). +## bioRxiv submission record (2026-10-24) + +- ID `BIORXIV/2026/761563`; New Results; Bioinformatics; CC BY 4.0; funder Knut and Alice Wallenberg Foundation (no award number). +- The uploaded PDF is `paper/build/fastaguard-biorxiv.pdf` (SHA-256 `8eb58ec7...2269`); bioRxiv served it back byte-identical. +- Form notes: one corresponding author only (Ehsan); accented letters must be HTML entities (`Umeå`); the abstract field takes the full + abstract; the built-in browser has no file-upload tool, so the file is picked by the user in the native dialog. +- Next: when bioRxiv issues the DOI, record it in `paper/submission.yml`, the cover letter and the academic dashboard + (`research-monitor`, entry SUB-03), then upload to ScholarOne. + ## OUP policy points that shaped the cover letter - Preprints allowed; must be disclosed in the cover letter; update with published DOI after acceptance; diff --git a/paper/submission.yml b/paper/submission.yml index 46f3645..774e95f 100644 --- a/paper/submission.yml +++ b/paper/submission.yml @@ -12,7 +12,7 @@ oup_template_deferred_to: revision submission_status: ready_for_author_upload author_gate_status: confirmed # 2026-10-24, reported in chat by Ehsan Estaji (Jian-Feng Mao knew of the work and agreed); no message from Jian-Feng seen by the agent zenodo_gate_status: deposited -biorxiv_status: not_submitted +biorxiv_status: submitted_in_screening # BIORXIV/2026/761563, submitted 2026-10-24, acknowledgement received; DOI pending scholarone_status: not_submitted # OUP policy: AI use must be disclosed in the cover letter AND the manuscript; preprints must be # disclosed in the cover letter; update the preprint with the published DOI after acceptance. @@ -110,6 +110,6 @@ required_human_inputs: blockers_for_machine_ready: [] blockers_for_human_submit: - - bioRxiv deposit (login by Ehsan; affiliation required) + - bioRxiv screening clearance and preprint DOI (then add it to the cover letter) - ScholarOne upload (credentials) - APC / institutional Read & Publish / ISCB discount check