Docs / About
Changelog
All notable changes to AMAP are documented in this file.
[Unreleased]#
Annotating H37Rv against its own reference now gives back every CDS of the AL123456.3 GenBank record at exactly its coordinates, strand and name: 4031 of 4031, against 4020 before. The changes apply to every genome, so results are not directly comparable with 1.0.1.
๐ฌ Correctness#
- Paralog copies no longer take another gene's place: a protein with a paralog was placed at both loci, and which copy survived came down to insertion order. On H37Rv itself that lost Rv1150, Rv1945 and Rv2828c; on M. bovis it reported genes the strain lacks (the mce3 operon of RD7, the phiRv2 prophage of RD11, plcBC of RD5) as copies sitting on their paralogs. Each placement is now checked against where its protein belongs, predicted from the sample's own offset to the reference so that indels do not throw it off
- Strand of a resolved conflict: taken from the protein's first placement anywhere in the genome instead of the one being resolved, so esxM (Rv1792), Rv3466 and two IS6110 copies came out at their own coordinates on the wrong strand; a Bakta call chosen over an alignment also took the alignment's strand
- Reference pseudogenes keep their extent: PE21 (Rv2099c) and PPE48 (Rv3022c), which the reference annotates without a stop, were extended to the next one, and oxyR' (Rv2427A) and Rv3216 lost the partial final codon the reference gives them
- The 1.0.1 fix for reference pseudogenes never fired: it looked for reference coordinates in a column that has not carried them since March 2026, and its tests wrote them in by hand.
check_cds.pynow reads the reference proteome instead - Alignments that stop short are completed: miniprot can miss a protein's N-terminus (Rv3611 on CP110619) or leave a repetitive C-terminus unaligned further from its stop than the 300 bp searched; the reference protein now says how much is missing
- Ties between copies of a gene go to the one Bakta also called, then to the one where the protein belongs by synteny, not the one that comes first in the genome (E. coli K-12 b2635)
- Draft assemblies: genes were compared on coordinates alone, and every contig starts at 1, so cut into 40 contigs CDC1551 had 87% of its proteins translated from the wrong contig. Every gene is now matched, translated and written on its own contig, and Bakta runs with
--keep-contig-headersso that its contig names match the FASTA's - Case 4 is grouped strand by strand: about 8% of its groups (101 of 1255 on H37Rv) put an alignment on one strand together with a Bakta call on the other
- A complete alignment is not displaced by a longer call in its frame: such a call ends at the same stop and only starts further upstream (plcD on CDC1551). Nor does a call that spans several reference genes win against them (Rv3022c and Rv3021c, one reading frame in CDC1551)
- Reference genes are protected again: the check looked for coordinates in identifiers that no longer carry them, so any call of similar length could displace a reference gene (Rv1614)
- Missing genes are recovered by what covers them: genes stored under Bakta locus tags looked missing by name, so a placement a codon off its gene went back in as a second copy. With the case 4 changes, ORFs annotated twice fall from 10 to 3 on CDC1551 and from 16 to 6 on M. bovis, where the reference has none
- Real copies of a gene are kept:
deduplicate_rvs.pydeleted every copy of a gene but one, 34 genes on E. coli O157:H7 Sakai, which were then missing from the annotation and from the duplication analysis. Further copies get identifiers of their own - Genes that lost their stop no longer swallow the next gene:
check_cds.pyextended a gene without a stop to the next one in its frame, which after an indel is the next gene's, so that gene was annotated a second time in the same frame (M. bovis Rv0590 over Rv0590A, Rv3373 over Rv3374, Rv2293c over Rv2292c). Such a gene now keeps its extent as a pseudogene, unless the extension only restores residues its reference protein has. ORFs annotated twice fall from 3 to 2 on CDC1551 and from 6 to 3 on M. bovis, and none of those left comes from an extension - Small proteins: miniprot placed none of the 33 proteins of 8 to 29 residues in E. coli K-12, and put the toxin IbsC (b4665) only on its near-identical neighbour IbsD. Every protein without an exact placement is now looked for in the six-frame translation of the genome, which takes K-12 against itself from 4252 to all 4286 of its genes, its alternative-start isoforms counted with their gene, and Sakai from 198 to 220 of 220
- Frameshifts and lost stops: a gene was extended to the next stop in its frame however far that was, and came out as a complete gene with a C-terminus its reference lacks; 18 in CDC1551 and 20 in M. bovis. It now keeps its extent and is flagged as a pseudogene when that stop lies more than ten residues past the end of the reference protein, as PGAP and Bakta treat a frameshift, which also covers a frame that runs into the next gene
- Genes a contig end cuts: in a draft assembly they came out as pseudogenes, or as complete genes, without a word. A gene whose frame runs off the contig, or that opens at its edge without a start codon, is now marked partial at that end. In CDC1551 cut into 40 contigs, the larger piece of each of the 25 genes a cut runs through is marked so
- The other pieces of a cut gene: miniprot reports only the placements within 5% of a protein's best, so only the larger piece of a gene a contig end cuts was annotated. What that placement lacks is now aligned again as a protein of its own, and its placement at the end of another contig is annotated under the gene's name and marked partial: 47 of the 50 pieces in the CDC1551 draft instead of 25, the other three being five or six codons long. A gene that opens at a contig's edge with a codon that can also start one, as one in six codons inside H37Rv genes can, is marked partial too when its protein begins inside its reference protein
- Disrupted copies of a gene: deduplication kept further copies only when intact, so a whole copy that is a pseudogene was deleted with the pieces of split genes; it now stays when nearly as long as the reference protein
- GFF3 references: each CDS line was translated on its own, so a CDS of several lines came out in fragments, and proteins under 11 residues were dropped to hide them. The lines are joined per CDS and translated with the bacterial code, reading
transl_except, and E. coli K-12's GFF3 gives the same 4318 proteins as its GenBank record, 24 of which were missing - Genes longer than their reference keep its name: a Bakta call that won its locus over a shorter alignment in its frame kept no name, and the protein was missing from the table: Rv2024c in CDC1551 and M. bovis, mmpL6 (Rv1557, whole in M. bovis where TbD1 cut it in H37Rv), Rv1068c, Rv1917c and Rv3135 in M. bovis, fliD (b1923) and b0282 in Sakai. The call now takes the name when the alignment spans at least half of the protein, so a remnant does not name the gene it ends (the 67 residues of cyp141 that RD12 leaves in M. bovis)
- Small proteins with a few substitutions: only exact copies were looked for, so Sakai's ten K-12 micro-proteins with one to four residues changed were not annotated. A protein of up to 50 residues with no exact copy is now looked for with up to a fifth of its residues changed; a stop codon in place of a residue counts, and the gene is then a pseudogene (b1229)
- Synteny no longer hangs on the order of the rows: the anchors were sorted by start and searched by midpoint, so two anchors starting together sent a placement to whichever window the row order gave, and adding unrelated rows moved Sakai's b2127 24 kb and handed its locus to the paralog b1162. A locus several proteins are placed at exactly no longer anchors either: the single IS6110 of M. bovis made 34 anchors with reference positions all over the genome, and came out under all 17 IS6110 names of each of its genes; they are now Rv2814c and Rv2815c, its own
- Genes without a COG category are counted once:
count_annot.pyfilled in a missing category asUnassignedand split it into letters, as it splitsJKinto J and K, so every gene eggNOG gave no category was counted once in A, D, E, G, I and U and twice in N and S, in*-AMAP.COG.tsvand in the report's COG figures and statistics, where it also shrank every other category's share. A test-profile run with a stand-in for eggNOG that gave none of its 4036 genes a category reported 4036 genes in each of those six and 8072 in N and S. Such a gene now counts once, asNaN, which the report shows as None, and its percentage is of all the genome's genes like every other category's - COG categories R, W, X, Y and Z in the report: its COG figures took their categories from a list that ran from A to V without R, so a gene eggNOG put in R, W, X, Y or Z was plotted as NA in the overview and in none of the per-category figures, which stayed empty for those five, X - Mobilome (prophages and transposons) among them. The categories now come from the report's own list of figures, A to Z and None, so each is plotted in its own. The statistics table never used that list and is unchanged
- The QC table counts the pseudogenes AMAP called:
annotation_qc.pyread the first ofIs_Pseudogene,pseudogeneandis_pseudogenethat the master table has, and every master table has Mycobrowser'sIs_Pseudogene, empty when Mycobrowser is off. Sopseudogene_countin*-AMAP.qc.tsvand the report's QC table was 0 in every organism-agnostic run (E. coli Sakai against K-12, where AMAP called 129 pseudogenes), and in an MTBC run the number of the sample's genes named after one of the 13 that Mycobrowser marks as pseudogenes in H37Rv, whatever AMAP made of them: 13 for CDC1551, where AMAP calls 123 and finds 6 of those 13 intact. It now counts AMAP's own call, the master table'spseudogenecolumn, the same genes the GFF3 markspseudo=trueand the GenBank/pseudo - The report's pseudogene chart counts AMAP's calls, sample by sample: its Pseudogene Detection section counted the master table's genes flagged in Mycobrowser's
Is_Pseudogeneor in AMAP'spseudogenecolumn, lineage by lineage, as "Pseudogenes per Lineage (Mycobrowser annotation)".Is_Pseudogenesays which genes are pseudogenes in H37Rv, not in the sample, so CDC1551 came out at 129 where AMAP calls 123 and the QC table says 123, the other 6 being genes Mycobrowser marks that CDC1551 has intact (Rv0947c, Rv1150, Rv2023A, Rv2098c, Rv3021c, Rv3324A), and without Mycobrowser the chart showed AMAP's calls under Mycobrowser's name. It also left out a lineage with none, and added up a lineage's samples, so a lineage with more samples looked as if it had more pseudogenes. REPORT_INPUTS now hands the report every master table row with the sample it is from, and the chart shows how many pseudogenes AMAP called in each sample, by lineage, a sample with none at 0, next to a table of them. The master table explorer shows thenotethat says why a gene is a pseudogene - A sample with no gene in a category counts 0 in the report:
count_annot.pywrites a row for each COG or Mycobrowser category a sample has genes in, and the report plotted the rows as they came, so a sample with no gene in a category was left out of that category's figures, of the Dunn tests between lineages run on them, of the overview and of the statistics table, instead of counting 0. Its lineage's box, mean and median were those of the samples that have some, a lineage none of whose samples has any dropped out of the comparison, and the comparison with the median never showed a sample that had lost a category. Each sample now counts 0 in every category another sample of the run has genes in, and the statistics table's Count is every sample of the lineage. A category no sample has keeps its empty figure, and*-AMAP.COG.tsvand*-AMAP.MycoBrowser.tsvare unchanged - The clean proteome leaves out the pseudogenes AMAP called:
export_clean_proteome.pytook them from the first ofIs_Pseudogene,pseudogeneandis_pseudogenethat the master table has, which is Mycobrowser's, and looked each protein up by its whole FASTA ID,L4_CDC1551_Rv0001;+;0-1524where the master table hasL4_CDC1551_Rv0001, so it never found one and its log said 0 pseudogenes. Only the proteins with an internal stop codon were left out, and the pseudogenes without one stayed in*-AMAP.clean.faa, which the docs say has none: 42 of the 123 AMAP calls in CDC1551 (a frameshift or lost stop codon, no stop codon within reach, no stop codon as in the reference) and 27 of the 129 in E. coli Sakai against K-12. It now leaves out the genes whosepseudogeneis yes, found by the ID before the;, and on both genomes holds the CDS the GFF3 does not markpseudo=true, 3891 and 5194, with the proteins the GenBank translates for them. A master table without that column stops it, where a warning let every pseudogene through
๐ Pipeline#
- Pseudogenes in the GFF3 and GenBank: the GFF3 never said which genes were pseudogenes, and the GenBank only for genes with an internal stop, though the master table knew. Both now carry AMAP's flag with its reason (
pseudo=trueandNote,/pseudoand/note), and mark the ends a contig cut off (partial=truewithstart_rangeorend_range,<or>); the master table and*-AMAP.tsvhave them as their last columns - The default H37Rv reference under Docker: its files live inside the container and were handed to processes as paths, so Nextflow mounted
/referencefrom the host, which Docker Desktop refuses and Docker on Linux mounts empty over the container's own.BUNDLED_REFERENCEcopies them out first -profile test:VALIDATE_INPUTSstill required the sample sheet the test profile does without. 1.0.1 had only been checked with-preview, which runs no task-resumekept results made by older code: Nextflow hashes a task's inputs and script, not the files it runs by path, so after an edit to the classifier or any script underbin/,-resumereused the old results. Every process that runs AMAP code now lists those files inext code, which puts them in its cache key; editbin/classify/cases.pyand TYPE_CASES and FIX_CDS run again while PATHOTYPR and Bakta stay cached- PATHOTYPR no longer depends on the classifier: its script said
pathotypr classify, and Nextflow adds anybin/entry a script names as a bare word to the cache key, so a change inbin/classify/re-ran PATHOTYPR, Bakta and everything after them. The subcommand is quoted - Nextflow 25.10 and later: the pipeline stopped before running anything, because the strict parser those versions use by default rejects the helper functions in
nextflow.config, the statements outside the workflow inmain.nf, the bareenv(SAMPLE_NAME)output andpublishDirpaths that name a task input. Nextflow 26.10 also refuses thereturnthat ended a--validate_onlyrun. The resource caps are now theresourceLimitsdirective, which still takes--max_cpus,--max_memoryand--max_timefrom the command line, a profile or a-cconfig. Nextflow 24.04, where that directive first appeared, is the new minimum --skip_mycobrowserin organism-agnostic mode: a custom reference from another genus turned it on by reassigning the parameter, which current Nextflow ignores with a warning, so the run went ahead with Mycobrowser enabled. It is now worked out in the parameter's default- Tasks leave the pipeline untouched: the first time a copy of AMAP ran, Python cached the bytecode of AMAP's own modules inside it, in
bin/classify/__pycache__, and withpathotypr classifynaming that directory in PATHOTYPR's script, the first-resumere-ran PATHOTYPR, Bakta and everything after them. Tasks now run withPYTHONDONTWRITEBYTECODE=1 - Samples with no Bakta call outside cases 2 and 3: when every Bakta call had an alignment at exactly its coordinates, which a small input can do, TYPE_CASES stopped with a KeyError, because the empty column of overlaps was read as a list of column names. Case 1 and case 4 now come out empty and the rest is classified as usual. An empty miniprot table stopped it the same way
- Samples Bakta calls no CDS on: with no contig names from Bakta to compare, the contig check took them for different names and stopped the run, and a Bakta table with nothing but its header did not read. The genes miniprot places now come out as missing from Bakta, like any gene it did not call
--version:--helpand the parameter docs have listed it since 1.0.0, but nothing read it, sonextflow run PathoGenOmics-Lab/AMAP --versionstarted a run. It now prints the pipeline and Nextflow versions and exits before any task- Drafts named after one of their contigs: pathotypr writes a summary row for each contig that carries lineage markers, and PATHOTYPR took the sample's name from the first, so CDC1551 cut into 40 contigs, sample
CDC1551d, came out asL4_contig_14, and so did every file after it. Two drafts whose first row named the same contig became one sample, the results of one overwriting the other's. A genome is now named<lineage>_<sampleId>from the sample sheet, the lineage of a draft being the one the markers of all its contigs add up to, and the contigs of a draft keep their names instead of the first one taking the sample's. A closed genome whose header is its sample ID comes out as before - Headers with a description: the name was the whole header line, so the tutorial's CDC1551 (
NC_002755.2 Mycobacterium tuberculosis CDC1551, complete sequence) was named with spaces in it, Bakta failed on it three times and the run finished without it, and without failing. Such a genome now takes its sample ID like any other, and so does the test profile's:L4_H37Rv_test, where it wasL4_CP110619_H37Rv - An input could be emptied: PATHOTYPR wrote the renamed genome next to the link Nextflow stages its input as, so when the new name was the file's own, sample
G26689inL4_G26689.fastafor instance, the input was overwritten with nothing and the run went on without it. The input is now staged in a directory of its own - Sample IDs with underscores: the steps after the correction told a sample's lineage from its sample ID by splitting its name at underscores, though typing only puts the lineage in front and sample IDs may hold underscores of their own.
L4_H37Rv_testwas counted as genomeH37Rv, the tutorial'sBeijing_1and aBeijing_2as one genomeBeijingin the category counts, the report and the duplication network, an untypedH37Rv_testas lineageH37Rv, and each gene of an untyped sample with no underscore as a genome of its own in the network. PATHOTYPR now says which lineage it named a genome after and every step is handed the sample ID and the lineage. The case summary gains aSamplecolumn and names an untyped sample as it is, notUntyped_<sampleId>, and the report no longer reads the first gene not in the reference as a header, nor stops when every gene has one - Genomes kept outside the run's folder:
VALIDATE_INPUTSstaged only the sample sheet and opened the paths written in it from inside its container, which sees only the folder that the work directory and the sheet share. Genomes anywhere else,/data/genomeswhile running from~/projectfor instance, were "FASTA file not found" and the run stopped before anything ran, and a relative path, which the sheet may use, was read from the task directory with or without a container. The genomes are now inputs of the process, each in a directory of its own so that two with the same file name do not collide, and each is read where Nextflow staged it - Runs with functional annotation stopped at validation:
VALIDATE_INPUTSlooked for--eggnog_dbin its own task directory, where the defaulteggnog_db, relative to the launch directory, never is, and took a database not built yet for an error, thoughEGGNOG_DBbuilds it. It now looks whereEGGNOG_DBbuilds it, staged when it exists, and stops the run only wheneggnog_db.okis there without a complete database, since Nextflow then never runsEGGNOG_DBagain - A Bakta database on the host:
--bakta_dbreached the tasks as a path the container does not mount. With-profile docker, validation called the database missing and stopped the run, and where nothing validated it, as under-profile test, every sample failed three times and was ignored, and the run ended with nothing annotated and exit 0. A database on the host is now staged intoBAKTA_ANNOTATOR,PREPARE_REFERENCEandVALIDATE_INPUTSlike any other input; the default inside the container is used as before - A warning on every run: validation expected a
db.sqlite3that no Bakta database has. It now checks forversion.jsonandbakta.db, which Bakta cannot run without, so their absence stops the run, and names the database's type and version --strict_validation: the documentation said validation warnings could be made to stop the run, but nothing passed the script's--stricton. The new parameter does- eggNOG's database under Docker:
EGGNOG_DBbuilt it in a folder it worked out for itself from--eggnog_db, and having no inputs, its container saw that folder only when Nextflow happened to mount it with the work directory and the pipeline'sbin/, through a parent folder they share. Otherwise, as when running from/data/projectwith the pipeline in~/.nextflow, the database was downloaded and built inside the container and lost with it, and onlyeggnog_db.okreached the host: later runs skippedEGGNOG_DB, andANNOT_EGGNOGMAP_BATCHfound nobacteria.dmnd. A database already there withouteggnog_db.okwas built again.main.nfnow makes the folder on the host and hands it toEGGNOG_DBas an input. A folder left with onlyeggnog_db.okneeds that file deleted, as validation says - eggNOG's database in a folder of any name:
ANNOT_EGGNOGMAP_BATCHasked eggNOG-mapper foreggnog_db/bacteria.dmndand--data_dir eggnog_db, but Nextflow stages a folder under its own name, so with a--eggnog_dbcalled anything else, as the HPC guide's/shared/databases/eggnogor the tutorial's~/databases/eggnog,EGGNOG_DBbuilt the database and the annotation stopped at "DIAMOND database eggnog_db/bacteria.dmnd not present", with or without a container. The folder is now staged under that one name, whatever it is called. The task's tag and first log line also gave a single sample's FAA size in bytes as the number of samples (batch_1519647_samplesunder-profile test) - Reference files on the host:
BAKTA_ANNOTATORgave Bakta--ref_annotation, or--bakta_proteins, as a path written into its script, which a container does not see, nor a task directory when it is relative, so the organism-agnostic mode failed under-profile docker: with K-12 as the reference for Sakai, Bakta called the "user proteins file" not valid three times, the sample was ignored and the run ended with exit 0. Likewise--pathotyper_snpsand--pathotyper_refon the host left every genome untyped, asNA_<sampleId>,--mycobrowser_refleft the Mycobrowser categories out, and one--miniprot_ref_*on the host while the others kept their defaults madeBUNDLED_REFERENCEfail, so nothing was corrected. A file on the host is now staged into the tasks that read it, each in a directory of its own, and a default is read inside the container as before - Reference files that are nowhere: a typo in one of those paths, or a default on a run without the container, went unnoticed until the samples were ignored or left untyped. Validation now checks each reference file the run reads and that is not on the host, inside the container where the tasks read it, and stops the run when it is not there. A missing Mycobrowser table only warns, since
GET_OUTPUTSgoes on without it - The pangenome without lineage typing: PANGENE aligned each sample's proteins onto the genomes PATHOTYPR renamed, so with
--skip_typing, which every custom reference from another genus needs, it had none and wrote empty files for every sample. It now aligns them onto the sample sheet's genomes, each copied to<sampleId>.fastafirst, so that two files of the same name (assembly.fastafrom two assemblies) stay apart and a.fnais not passed over - Genes not in the reference, sample by sample: the report counted only the samples that had some, so a sample whose genes all matched the reference dropped out of its lineage's box instead of counting 0, and when no sample had any (H37Rv against itself, as in
-profile test) the section was an empty plot. Every sample is now counted, the section says so when none has any, and each point is drawn at its count, on an axis from 0, where it was jittered up and down by as much as 0.4 - The report's pangenome section lost a genome: pangene names each genome
<name>#0, and the report read the merged matrix with R's default comment character, so its header stopped at the first#. One genome column was dropped and the gene names became row names, so the core/accessory and frequency plots used a single column and the heatmap never appeared. The missing-gene and duplication tables, which carry the reference annotation's gene IDs as given, are now read without a comment character too - Genes across the origin of a circular sequence: Bakta reads a sequence as a complete replicon when its FASTA description says
complete(NCBI's "complete genome" and "complete sequence") orcircular=true(Unicycler), joins its ends and calls a gene across its origin from its start to its stop, start after end. TYPE_CASES stopped on such a gene: E. coli O157:H7 Sakai with NCBI's headers, whose stcE runs across the origin of plasmid pO157 (92563..2502), failed three times and was left out, and the run finished without failing. Past it,check_cds.pydropped such a gene as invalid coordinates, deduplication and the QC table gave it a negative length, the GFF3 wrote it backwards, the GenBank writer stopped on it and the Panaroo GFF3 spanned it over the whole sequence. It now goes through whole and is written as Bakta writes it: start after end in the tables,join()across the origin in a circular GenBank record, and in the GFF3 its end plus the sequence's length on a region markedIs_circular=true. A gene near the origin of such a sequence is read across it too, so Rop on plasmid pOSAK1, of which Bakta called the first 141 bp, comes out whole. Annotated against K-12, Sakai with NCBI's headers now has 5331 genes in common with the same genome under bare names, which Bakta reads as linear, and differs in stcE and Rop, whole where the linear reading has 804 of stcE's 886 residues and no Rop, and in two phage genes Bakta starts elsewhere on a complete genome - Typed closed genomes were annotated as linear: PATHOTYPR renamed a genome of one sequence to
<lineage>_<sampleId>and nothing else, so the description Bakta reads a complete, circular replicon from never reached it, whatever the header said, and a gene across the origin of a closed genome came out as two pieces under one name, marked 5' and 3' partial. Only the sequence's ID is replaced now, and the description after it stays, as with--skip_typing. Typed from NCBI's headers, CDC1551, CP110619, AL123456.3 and M. bovis get the same genes as before, in place, name, flags and protein, though Bakta starts a few of its own calls elsewhere on a complete genome (dnaA at 1 instead of 34), so the gene IDs that are Bakta locus tags can be numbered differently. H37Rv turned so that the origin cuts gyrB, gyrA or pknB gives back all 4031 GenBank CDS, where it gave 4030 with the cut gene in two pieces. The GenBank record of such a genome is marked circular, with the description in its definition, and its GFF3 has a region markedIs_circular=true - The report's duplication summary: it printed the number of homologous community pairs as markdown from a chunk that did not pass its output on as markdown, so the report showed
## **48** homologous community pairs detected across all genomes.in a code box. It is now a sentence. The example report's duplication table had columnsGET_ALLDUPnever writes, so its Network Metrics tab said there were none; the demo data now has the columnsGET_ALLDUPwrites and the example is rendered again - Modularity in
homologous_communities.tsv:Modularity1andModularity2were computed with the whole community as its only group, which makes them 0 whatever the community, as on all 29 rows of CDC1551 against M. bovis. The two columns are gone - COG categories in
homologous_communities.tsv: with numpy 2, as in the container,Elements1andElements2read{'L': np.int64(4)}, and they counted each protein eggNOG gave no category in category+or-, since its ID keeps the strand FIX_CDS wrote. They are now plain counts, the largest first and ties in alphabetical order rather than in an order that changed from run to run, and a protein without a category counts as-, eggNOG's mark for none - The order of
-profile: from Nextflow 25.10, whose strict parser applies the profiles in the order-profilenames them and lets the last one win, the laptop caps of the container profiles took over from the others:-profile slurm,singularityheld every task of a cluster run to the launching machine's CPUs and 16 GB, where-profile singularity,slurmgave it 48 CPUs and 128 GB,-profile test,dockerran the test with 16 GB and-profile highmem,slurmwith 128 GB. No profile sets a cap now: they are set after the profiles, from whetherhighmem,testor a scheduler profile is on, so every order gives what Nextflow 24.04 gave. The parameter docs gave 24 CPUs and 50 GB as the defaults, which no run got - The duplication files are the same from one run to the next:
homologous_communities.tsvlisted its pairs of communities in the order of a set, which changes with Python's hash seed from one run to the next, and with it which community of a pair came first and the last digit ofAvgClustering, an average over the community's proteins that networkx went through in the order of their set: 0.5833333333333333 or 0.5833333333333334 for the same pair of CDC1551 and M. bovis. The pairs now come in the order of the communities, the sample first by name on the left, and the metrics go through the proteins in alphabetical order. Each family induplications_*_groups.tsvlists its proteins in alphabetical order, where they too came in the order of a set, and each sample's two network drawings no longer start from a random layout and a random partition, so every file both steps write is the same run after run - No web browser: both duplication steps wrote their networks with pyvis'
show(), which also opens the file in a web browser, so a run without a container on a desktop opened a browser tab for every network. They are written as before, without being opened - A custom reference with a gene across the origin, or with several sequences:
PREPARE_REFERENCEnamed each reference protein after where its pieces lie, so tagA on plasmid pO157 of E. coli O157:H7 Sakai,join(92527..92721,1..2502)in NCBI's GenBank record, becamepO157p01_1-92721, the whole plasmid, and lost its stop; from NCBI's GFF3, which writes it 92527..95223, it came out as its first 65 of 898 residues. Bakta's stcE on the reference, 92563..2502, matched no gene and was listed among the false positives, and the calls were compared with the genes of every sequence at once: seven calls on pO157 and two on the chromosome counted as matched by genes of the other at the same numbers, and the GenBank's 1-92721 hid two more. With Sakai as its own reference, the sample with NCBI's headers came out with tagA as 1..2502, 65 codons short and not marked partial (GenBank), or from Bakta's later start (GFF3), and with bare headers the GFF3 lost the 2502 bp piece altogether. A gene across the origin now ends past the sequence's last base, as GFF3 writes it (pO157p01_92527-95223, with its stop), each protein names its sequence (contig=), Bakta keeps the FASTA's names on the reference, and each call is compared with the genes of its own sequence, on both sides of the origin; calls on a sequence the annotation has no gene on are not judged. Both formats now give the same 5155 proteins and the same 260 false positives, and the sample with NCBI's headers gets tagA exactly, 92527..2502. K-12's 142 false positives are unchanged pipeline_info/params.ymlis the run's own: with -resume it came back from the cache, so it held the first run's parameters whatever later runs were given, since its script readsparamsas a whole, which Nextflow 26.10's default parser leaves out of the task's hash. And all but the first parameter fell outsideparams:, with a strayYAMLline at the end, because Nextflow strips a script's indentation once the script is filled in. DUMP_PARAMS now never comes from the cache, and writes each value as a JSON string underparams:- Bakta's SLURM QoS only on SLURM: Bakta's jobs asked for
--qos=mediumunder every scheduler, though only SLURM's sbatch takes it, and under-profile sgeit took the place of the memory request the sge profile gives every task. The slurm profile now sets it for Bakta - The Mycobrowser table is checked whenever it is read: validation checked
--mycobrowser_refonly with the functional annotation, though the interactive report reads it in every run, so under--skip_functional_annotationa wrong path went unwarned and the report came out without Mycobrowser's categories - The duplication networks draw offline: both duplication steps loaded their networks' drawing library from a CDN, so without the internet the pages drew nothing. The library now goes inside each page, about 0.7 MB
environment.ymlpins eggNOG-mapper 2.1.13, the version the container ships, where it said 2.1.12
๐ Report#
- A report organised by question,
results/amap_report.html: next tofinal_report.html, a new process,AMAP_REPORT, writes one self-contained file from each genome's own outputs: a table of the genomes with the values far from comparable genomes flagged, a map of every reference gene in every genome where regions of difference show as blocks, a lookup of one gene in every genome, the correction from Bakta's calls to AMAP's genes with the genes behind each step, the pseudogenes by lineage, what lies outside the reference, and the functional profile, the categories that differ most between lineages first. It needs only what the correction leaves, so a run with--skip_functional_annotation, which gets nofinal_report.html, gets this one, and it names the genomes of the sample sheet that produced no annotation. Fifteen MTBC genomes make 0.5 MB (final_report.htmlis 4.4 MB for twelve); it draws everything with its own JavaScript and needs only Python's standard library to write
๐งน Code#
reconcile_coordinatesis gone: it read reference coordinates from protein identifiers that have not carried them since March 2026, so it changed nothing. The paralog check does its job with the sample's own offsets
๐ Documentation#
- A website in plain HTML, drawn with real genes: MkDocs is gone. The front page starts a newcomer in three steps and builds the command for their machine, the pipeline is a metro map whose stations are
main.nf's processes, and the four cases, the missing genes and the harder cases (paralogs, small proteins, contig ends, frameshifts, copies) are drawn on genes of CDC1551, M. bovis and E. coli from the benchmark. The tutorial and user guide stay markdown, readable on GitHub, andtools/build_docs.pyturns them into pages of the same site, dark by default - The site is checked against the code:
tools/check_site.pyruns every drawn window throughbin/, compares the thresholds on the pages with the constants in the code, the metro map withmain.nfand every option with the pipeline's parameters, so a change that makes the site wrong fails the docs workflow.tools/figure_data.pyredraws the windows from a benchmark run - What the docs said that the pipeline does not do: with
--skip_functional_annotationa run still writesAMAP/, with the corrected table and sequences, where the outputs page said it did not; no step writesPATHOTYPR/renamed_fastas.tsv; and the 19-residue toxin the algorithm page names is IbsC (b4665), not an ldr toxin - A drawing on every page that explains something: the duplication family and communities of CDC1551 and M. bovis, from a real run of both duplication steps; the sample sheet with its tabs and the messages the validator prints; the ranges a genome is validated against; the container and what it holds; a run on a cluster; what happens to a task that fails; the report section by section; the five outcomes and the three inputs of a decision; the command builder on the quickstart. Most are built from the code or the config, so they change when it does
- The benchmark on the front page: the seven genomes whose answer is known, and how many of their genes AMAP finds, checked against
tests/benchmark/README.md. A shared link to any page shows a card drawn from the front page's genes - More that the docs got wrong: the duplication page listed ten network metrics, the table has four, and its columns ending in 1 and 2 are the two communities of a pair, not two levels; Nextflow needs Java 17, not 11;
--skip_functional_annotationleaves no GFF3 or GenBank, which come after eggNOG; tasks are capped at 16 GB on one machine and at 128 GB under a scheduler profile, not 50 GB; and lineage typing is not skipped on its own for other species, only Mycobrowser is - The tutorial's numbers are today's: its case counts and QC line, and their copies on the front page, the outputs page and the custom-reference page, came from runs in July. They now come from the tutorial's own commands run again on 18 October 2026 with
--skip_functional_annotation, the QC line from GET_OUTPUTS' own script run on that run's corrected genes: CDC1551 has 4014 genes and 123 pseudogenes as AMAP calls them, where the old line counted Mycobrowser's 13, and Sakai keeps 1387 genes K-12 lacks, not 1356. The tutorial also says where--validate_onlywrites its verdict and where the number of reference proteins extracted is, as neither reaches the console - The README in the site's style: the metro map and the five outcomes in light and dark, the benchmark's scores, and the links a newcomer needs first
- The README opens with the logo, and its drawing plays: the picture at the top was a still of the front page's drawing, dark whatever GitHub's theme. It now plays the drawing's four steps, in light or dark as the theme is, made by
tools/pictures.mjsfrom the front page's own figure. The README's metro map gains the interactive report's station - One DOI: the README, CITATION.cff and every page of the website cited the DOI of one version on Zenodo, and the manifest,
main.nfand the interactive report the concept DOI, which always resolves to the latest version. All of them cite the concept DOI now - Bakta's options on a SLURM cluster: the HPC page says how to give Bakta a site's options, since for Bakta the QoS the slurm profile sets takes the place of
-process.clusterOptions - The website is served as it is built: GitHub Pages serves
docs/from main, and by default runs Jekyll over it first, which would have made a page of each markdown file with front matter at the address of the pagetools/build_docs.pybuilds from it.docs/.nojekyllturns Jekyll off, andtools/check_site.pyfails without it
๐งช Testing#
tests/selftest/h37rv_selftest.shannotates H37Rv with itself inside the container and fails unless every GenBank CDS comes back exactlytests/test_cache_keys.pyfails when a process runs AMAP code that is not in its cache key, or when its script names abin/entry as a bare word without declaring ittests/benchmark/benchmark.shscores the correction algorithm on seven genomes whose right answer is known (H37Rv twice, CDC1551 complete and in 40 contigs, M. bovis, E. coli K-12 and Sakai), downloading them and running Bakta and miniprot in the container the first time, and lists every gene that changed since an earlier run. A gene whose sequence changed counts as found when a gene with its name covers half of its own bases there, not half of the stretch it lands on, which an insertion can make far longer (63 kb with a prophage inside for b1004 in Sakai); a gene mostly deleted is not counted. Changed genes found: 50 of 50 in CP110619, 860 of 862 in CDC1551, 1515 of 1515 in M. bovis and 3363 of 3367 in Sakai, and the README says why each of the other six is nottests/test_name_by_lineage.pyruns PATHOTYPR's own script, rendered as Nextflow renders it, with a stand-in for pathotypr that writes the summaries pathotypr wrote for CDC1551 complete and in 40 contigs, and fails unless Bakta would read a renamed genome as circular exactly when it reads the original sotests/test_sample_ids.pyruns TYPE_CASES, GET_OUTPUTS, GET_COUNT, GET_DUPLICATIONS, REPORT_INPUTS and GET_ALLDUP as Nextflow renders them, chained as the pipeline chains them, for a typed and an untyped run whose sample IDs hold underscores, and then the report's own R code where R is installed, and FINAL_REPORT's own script on a run's tables and on one that leaves every table it can emptytests/test_validate_inputs.pyrunsVALIDATE_INPUTS's own script, rendered as Nextflow renders it, in a task directory laid out as Nextflow lays one out: genomes in a folder the task cannot see, relative paths, two genomes with the same file name and a sheet of 500 samples; and checks both databases, eggNOG's in each stateEGGNOG_DBcan leave it intests/test_eggnog_db.pyrunsEGGNOG_DB's own script, rendered as Nextflow renders it, with its folder staged as Nextflow stages an input and stand-ins for eggNOG-mapper's download and build: the database is built in that folder whatever it is called, one already there is used as it is, and a shortbacteria.dmndis built againtests/test_eggnog_annotation.pyrunsANNOT_EGGNOGMAP_BATCH's own script the same way, with a stand-in foremapper.pythat looks for the database as eggNOG-mapper does: the database is found whatever its folder is called, a folder calledsplit, where the script writes each sample's annotations, is left as it was, and the tag and log count a single sample as onetests/test_reference_files.pyruns the scripts ofBAKTA_ANNOTATOR,PATHOTYPR,GET_OUTPUTSandBUNDLED_REFERENCE, rendered as Nextflow renders them, their own Groovy included, with stand-ins for Bakta and pathotypr and each reference file on the host, under a relative path or inside the container; and fails when a process reads a reference file's param other than as what it falls back on when nothing is staged; and checks the Mycobrowser table whenever a step reads ittests/test_pangene_genomes.pyruns PANGENE_GENOME and PANGENE's scripts as Nextflow renders them, with stand-ins for miniprot and pangene, and fails unless each sample is aligned onto every other sample's genome and never onto its own, typed or nottests/test_report_tables.pymerges two Rtabs as PANGENE writes them and checks that the report reads every table a#can reach without a comment character; where R is installed, as in the container, it also runs the report's pangenome code on the merged matrixtests/test_cog_counts.pycounts COG categories ascount_annot.pydoes, for genes in none, in two and in lowercase, then runs the steps after the correction astests/test_sample_ids.pychains them, with eggNOG giving some genes no category, then no gene one, then some samples no gene in a category another sample has genes in, and reads what the report's own overview, per-COG figures and statistics table show where R is installed: each category in a figure of its own, with every sample, at 0 where it has no gene. The Mycobrowser figures, statistics and ratios to the median are read the same way for a sample with no gene in one of their categoriestests/test_duplication_report.pyruns GET_DUPLICATIONS and GET_ALLDUP as Nextflow renders them, chained as the pipeline chains them, with eggNOG giving one copy of a duplicated gene a category, another none and the last no row at all, and checks the columns and COG counts ofhomologous_communities.tsv; where R is installed, it knits the report's duplication summary on that table and on the oneGET_ALLDUPwrites when nothing is duplicated. It also fails when a chunk of the report prints markdown without passing it on as markdowntests/test_profile_caps.pyfails when a profile sets a cap, or when the order of-profilechanges one: it readsnextflow.configand applies its profiles in every order, as both of Nextflow's parsers do, and with Nextflow on PATH also asksnextflow configtests/test_qc_pseudogenes.pyruns the steps after the correction astests/test_sample_ids.pychains them, with FIX_CDS calling some genes pseudogenes and Mycobrowser marking another, with Mycobrowser on and off, and checks that*-AMAP.qc.tsv, REPORT_INPUTS and the report's own QC table count the pseudogenes AMAP called, as many as the GFF3 and the GenBank marktests/test_duplication_reproducible.pyrunsget_duplications.pyandnetwork_genomes.pyunder three hash seeds, on the Diamond tables of three genomes with a family whose average clustering changes with the order of its proteins, and fails unless every file they write is the same under all three; it also fails when either script opens a web browser, with hits or without, as a stand-in browser that records what it is asked to open tells, and when a network page asks a CDN for its drawing librarytests/test_custom_reference.pyextracts a reference whose plasmid has a gene across its origin from a GenBank record and from GFF3 written either way, on either strand, compares Bakta's calls with it sequence by sequence, and runsPREPARE_REFERENCE's own script, rendered as Nextflow renders it, with a stand-in for Bakta that names sequences as Bakta doestests/test_amap_report.pywrites the report from four genomes of a real run cut to two stretches of the reference (tests/data/report/, rebuilt by itsmake_fixture.py), where M. bovis lacks RD4 and a contig end cuts dnaE2, plus a gene across the origin of a circular plasmid, and checks that every gene, and every Bakta call AMAP did not keep, comes back out of the page as it went in, with Python and, where node is installed, with the page's own JavaScript, along with the states, the flags, the correction's steps and what the process hands the scripttests/test_dump_params.pyruns DUMP_PARAMS with Nextflow where it is installed, under both parsers, with values holding quotes,$, backticks, a backslash and a line break, and again with -resume and one value changed; everywhere, it fails if the step can come from the cachetests/test_cluster_options.pyreads which profile gives Bakta the QoS, and with Nextflow resolves Bakta's and every task's cluster options for each scheduler, in both orders with a container and under both parserstests/test_environment_pins.pyfails whenenvironment.ymlpins a tool at a versiondocker/Dockerfiledoes not installtests/test_citation.pyfails when anything cites AMAP with a DOI other than the concept DOI of the manifesttests/test_clean_proteome.pyruns the steps after the correction astests/test_sample_ids.pychains them, with FIX_CDS calling some genes pseudogenes, one with a stop codon in its protein and the others without, and Mycobrowser marking a gene AMAP finds intact, with Mycobrowser on and off, and checks that*-AMAP.clean.faaholds every protein FIX_CDS wrote but the pseudogenes', as it wrote them: the CDS the GFF3 does not markpseudo=true, with the GenBank's translationstests/test_report_pseudogenes.pyruns the steps after the correction astests/test_sample_ids.pychains them, with FIX_CDS calling a different number of pseudogenes in each sample, none in one, and Mycobrowser marking a gene that every sample has intact, with Mycobrowser on and off, and checks that REPORT_INPUTS hands over each master table row with its sample; where R is installed, it runs the report's own pseudogene section on what REPORT_INPUTS made and checks that it draws each sample at the number AMAP called, the QC table's, a sample with none at 0, under a title that does not name Mycobrowser- 744 tests, up from 92
[1.0.1] - 2026-07-25#
Bug fixes, most of them found by running the pipeline on real genomes rather than by reading it. Several changed what the pipeline outputs, so results from 1.0.0 are not directly comparable.
๐ฌ Correctness#
- Genes no longer vanish during overlap resolution:
_insert_with_dedupqueued entries for eviction while scanning for conflicts, and applied them even when it then abandoned the insert, so genes were deleted with nothing put in their place - Reference pseudogenes are recognised for any organism: the check was gated on the identifier starting with
Rv, so on every other organism every reference pseudogene was extended to the next stop and lost its flag. The same gene came out differently depending on what it was called - The extracted reference proteome carries its stop codon:
extract_ref_proteins.pywrote coordinates spanning the stop but a protein without it, so miniprot's alignment ended three bases short of every Bakta CDS and no gene could ever reach case 2. On E. coli O157:H7 this moved 3325 genes out of conflict resolution - Mycobrowser join key: built with
rvs.split('_')[0], which returned the whole string for a gene with several references (Rv0094c, Rv3467) and truncated any name containing an underscore (PE_PGRS1becamePE). 44 more genes get a functional category on a real MTBC sample, none lose one - Annotation table and FASTA files agree:
check_cds.pywrote the FASTA files from dictionaries filled before its final deduplication, anddeduplicate_rvs.pymatched FASTA headers on the gene id alone, so removing one copy of a multi-copy gene deleted all of them - Untranslatable genes are dropped from the table, not left in it with no sequence anywhere
๐ Crashes#
--skip_mycobrowserand organism-agnostic mode: the placeholder frame was built with a duplicateLocuscolumn, sopd.mergerefused to run andGET_OUTPUTSfailed on every run without Mycobrowser- Miniprot PAF parsing: pandas inferred the column count from the first row, and miniprot varies its trailing tags per alignment, so any file whose first row was narrower aborted the parse
- Biopython 1.85 and newer:
SeqFeature.strandwas removed;to_panaroo.pyandextract_ref_proteins.pynow read it off the location -profile test: pointed at a sample sheet naming paths that are not in this repository. It now annotates the reference genome bundled in the container, so it needs no data and nothing downloaded
๐ Silent data loss#
- First Diamond hit:
read_and_createleft header inference on while stripping#comments, so the first alignment became the column names and disappeared from the duplication analysis - Multi-part gene names in
rename_fastas.py: headers were cut at the third underscore, collapsing every PE/PPE gene of a sample onto one identifier - eggNOG annotation rate: counted with
notna(), and emapper writes-for fields it could not fill, so the QC metric read close to 100% whatever the run had done
โก Performance#
reconcile_coordinatesis linear rather than quadratic: it scanned every protein and every reference for each gene, calling a regex on each candidate. Indexed by gene name up front, a 4000-gene genome goes from 18.3 s to 0.011 s with identical output over 200 randomised genomesdeduplicate:list.count()per element and a full scan per duplicated gene, replaced with a Counter and a single grouping pass
๐ Report#
- Layout: the stylesheet was losing to the one rmarkdown inlines afterwards, so the article sat underneath the table of contents and the left logo on top of the title
- Self-contained without a network at render time: jQuery came from a CDN and the second logo from GitHub. pandoc inlines those when the render machine has a network and leaves them dangling when it does not, so a report rendered on an isolated node arrived with a broken image
- Half the size: 8.2 MB to 4.4 MB, by shipping only the plotly traces actually used and quantising the figures
- The version in the report is the version that produced it: it was written by hand in three places in the Rmd, so any release that forgot to update them published a report naming the wrong version. It now comes from the manifest in
nextflow.config
๐ Documentation and infrastructure#
- Documentation site built with MkDocs Material, published to GitHub Pages, including a tutorial with the numbers from real runs and a rendered example report
- Docker image workflow: builds, smoke tests and publishes on a
v*tag or on demand - Tests workflow enabled: it had been sitting disabled and would have failed, since its dependency list was missing plotly and its syntax check had drifted five files behind
requirements-test.txt: one dependency list for CI, the docs and a local checkout.environment.ymlwas missing the Pythonplotly, so a conda environment built from it could not startclassify_cases.py- 92 tests, up from 58
[1.0.0] - 2026-04-01#
๐ฌ Correctness#
- Strand-aware gene dictionary:
combined_dictkeys changed from(Start, End)to(Start, End, Strand): genes on opposite strands at the same coordinates no longer collide silently - Consistent 1-based lengths: all length calculations now use
End - Start + 1(15 sites fixed) - Robust ID cleaning: regex
_\d+-\d+$replaces brittle 4-part underscore rule, so PE_PGRS genes are handled correctly - Exact gene name matching: prevents false matches (Rv3508 โ Rv3508c, Rv1 โ Rv10)
- IntervalTree half-open intervals: all 9 operations corrected from
[Start:End]to[Start:End+1] - Cross-strand gene preservation: merge operations now include Strand to prevent loss
- Paralog preservation: multi-copy PE/PPE genes no longer collapsed during groupby
- Pseudogene propagation: Is_Pseudogene column now reaches master table and report
๐ก๏ธ Robustness#
- Pre-flight validation: new
VALIDATE_INPUTSprocess checks FASTA format, N-content, GC%, genome size, duplicate headers and database paths, before any computation --validate_onlymode: check all inputs without executing the pipeline- eggNOG DB integrity:
bacteria.dmnd<100 MB detected as corrupted โ automatic rebuild - Bakta DB pre-check: actionable error message with download command
- OOM retry: memory scales with
task.attempt(e.g., 50 GB โ 100 GB โ 150 GB) - 0-protein handling: eggNOG batch handles empty inputs gracefully
- Completion handler: prints duration, succeeded/failed/ignored counts
๐ Reproducibility#
nextflow.manifest: version 1.0.0, Nextflow โฅ23.04.0, DOI, homepageSOFTWARE_VERSIONS: YAML + TSV with exact versions of 12 toolsDUMP_PARAMS: complete parameter dump per run- Diamond sorted output: deterministic results regardless of thread count
environment.yml: Conda environment with pinned versions- Louvain seed: fixed to 42 for reproducible community detection
โก Performance & HPC#
- Dynamic resource scaling: memory grows with each retry attempt
- Node-local scratch:
--scratch true(default on SLURM/SGE/PBS/LSF) - Diamond block size tuning: auto-calculated from available RAM
- Bakta local temp:
--tmp-diron node-local disk (avoids NFS metadata storms) - eggNOG batch mode: single run for all samples (10ร faster, 50ร less filesystem I/O)
- Thread utilization: Bakta 12, Diamond 6-24, Miniprot 2ร3, Pangene 2รN
- SLURM/SGE/PBS/LSF profiles: with time limits, queue sizes, rate limiting
storeDirfor eggNOG DB: persists across runs, survivesnextflow clean
๐ Scalability#
find -print0 | xargs -0: replacesecho|tr|grepin REPORT_INPUTS (handles 500+ files)publishDir mode: 'link': hardlinks instead of copies for per-sample outputscache 'lenient': tolerates NFS timestamp drift between cluster nodes- awk
close(): prevents file handle exhaustion on 500+ sample eggNOG splits - JVM tuning docs: NXF_OPTS guidance for large runs
๐งฌ Organism-Agnostic#
- Custom reference:
--ref_annotation+--ref_genomereplaces built-in H37Rv - Auto-detect mode:
--skip_mycobrowserauto-enabled when genus โ Mycobacterium - Dynamic reference comparison: report uses median distribution (not hardcoded H37Rv)
- Auto-extending color palette: non-MTBC lineages get colors automatically
- Generic labels: "Ratio to Median" instead of "Ratio to H37Rv"
- Manifest updated: "Assisted Microbial Annotator Pipeline"
๐ Output Quality#
- Clean proteome:
{sample}-AMAP.clean.faawithout pseudogenes or internal stops - Annotation QC: per-sample metrics (genome size, GC%, coding density, hypothetical %, annotation rate)
- GFF3 spec compliance:
##sequence-regionpragmas, attribute escaping, sorted features - QC report section: interactive table + coding density / hypothetical % / genome size plots
๐งช Code Quality#
- 58 unit tests: covering IDs, overlaps, reconciliation, validation, clean proteome, QC, merge
- GitHub Actions CI: Python 3.9 + 3.11 matrix (currently disabled, ready to activate)
- Type hints: core modules with
Coord,Coord3,CoordDictaliases - Module docstrings: coordinate conventions, naming schemes documented
๐ฅ๏ธ Usability#
- Structured startup banner: shows all configuration in a formatted box
--versionflag: prints pipeline + Nextflow version- Progress logging:
โธ [BAKTA] Annotating G26689...for every major step - Actionable error messages: boxed errors with cause + fix + re-run command
- Sample count at startup: confirms TSV was parsed correctly
๐ Documentation#
- README: updated for v1.0.0 with all new parameters, outputs, and organism-agnostic usage
- CHANGELOG: this file
- Output tree: documents
.clean.faa,.qc.tsv,pipeline_info/new files
๐ Bug Fixes (85 total)#
- 20 critical (๐ด) + 65 improvements (๐ก)
- See commit history on
fixbranch for full details
[0.1.0] - 2025#
- Initial release
- MTBC-focused annotation pipeline
- Bakta + Miniprot + eggNOG integration
- HTML report with COG, Mycobrowser, pangenome analysis