Skip to content

Send FASTA header delimiters, so the download matches the preview - #1482

Merged
olsonanl merged 5 commits into
BV-BRC:alphafrom
olsonanl:feature/fasta-header-formats
Oct 30, 2026
Merged

olsonanl merged 5 commits into
BV-BRC:alphafrom
olsonanl:feature/fasta-header-formats

Conversation

@olsonanl

Copy link
Copy Markdown
Contributor

The bug

The download wizard's FASTA preview promised |-separated description fields. The download delivered spaces.

preview:   >patric_id| strain | host_name | isolation_country
download:  >fig|1545742.911.peg.9| MGYG-HGUT-00869 Human USA

Two independent hardcodings that disagreed:

// FASTAConfiguratorStep.js:358 — preview
previewParts.push(descFields.join(' | '));

// p3_api util/fastaHeaderFormatter.js — server
const description = formatFields(doc, config.descriptionFields, ' ')

The server's delimiter was a literal argument, not a parameter, so no request could change it. It now accepts http_fasta_description_delimiter — this PR sends it.

after:  >fig|1545742.911.peg.9| MGYG-HGUT-00869 | Human | USA

Verified against alpha; the rendered header now matches the preview exactly.

What changed

  • Send http_fasta_id_delimiter and http_fasta_description_delimiter. The ID delimiter goes at the server's own default (|), so nothing changes for a user who doesn't touch it. The description delimiter is ' | ' — what the preview has always shown, and what makes a multi-field description parseable, since several values (host_name, product) contain spaces of their own.
  • Derive the preview from the same values rather than a second hardcoded string. Also escapes it, since field names and delimiters land in innerHTML.
  • Collapse duplicated parameter-building. Three places built these params — the form-POST path, the xhr path, and the in-browser convertToFasta, each with its own copy of the delimiters. The first two are now one buildFastaParams(); the third reads the configured values instead of hardcoding '|' and ' '. That duplication is how the preview drifted from the download in the first place, and the third copy would have kept producing the old output on the client-side route.

Absent vs empty

An absent delimiter is omitted — leave the server's default alone. An empty one is transmitted, because "" is a meaningful delimiter meaning "run the fields together". Hence !== undefined rather than a falsiness check, which would silently drop that choice.

Testing

8 new tests covering both request paths, with one asserting they agree. Suite at 171 passing; lint unchanged from baseline.

Mutation-tested rather than assumed: removing the delimiter-sending makes exactly 4 of the 8 fail. The other 4 guard adjacent behaviour (field params, absent-delimiter omission, no-config) and correctly stay green.

Manual check — a FASTA download from a genome_feature grid with several description fields selected should now produce a header matching the preview character for character.

Scope

First of five steps in PLAN-fasta-header-formats.md, included here. The rest — delimiter controls in the UI, format-string mode (http_fasta_header), saved named formats in the workspace, and replacing the hardcoded field list with /fields — build on this but are not in this PR.

REPORT-fasta-header-presets.md is also included: seven NCBI and GISAID/augur format strings verified against live alpha, with real output and the data traps behind them (subtype carries Influenza B for type-B records; strain is empty on ~2% of influenza genomes and leads the GISAID format; {patric_id} contains a pipe).

🤖 Generated with Claude Code

olsonanl and others added 5 commits October 30, 2026 16:53
The download wizard's preview promised "|"-separated description fields
while the server joined them with a space, because the delimiter was a
hardcoded argument rather than a parameter. The API now accepts
http_fasta_description_delimiter and an http_fasta_header format string,
both verified live on alpha.

Plans the client work in five independently shippable steps, starting
with sending the delimiters -- which fixes the reported bug on its own
and needs no new UI.

Records two measured divergences from the documented format-string
contract: an unknown field renders empty and returns 200 rather than
400, and {{...}} escaping drops its contents. The first is why
client-side validation against /fields is load-bearing here rather than
merely convenient -- a typo'd {hostname} currently yields a silently
truncated header with no error anywhere.

Named formats go in /<user>/home/.preferences/fasta-formats.json,
following FavoriteFolders (ensure-dir, mod-time merge, per-user cache);
the save UI is hidden when logged out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NCBI's defline convention is documented (>SeqID [modifier=value], no
spaces around '='); GISAID's EpiFlu builder is login-only, but the
pipe-delimited order it emits is pinned by `augur parse`, whose
documented example gives strain|type|subtype|date|season|country|host|
accession.

Both render on alpha today, except the flu one: subtype, segment,
lineage, clade, genbank_accessions and species exist on `genome` but are
absent from `genome_feature`'s enrichment set, which is bacterial-
metadata shaped. The result is the silent-truncation failure again --
{strain}|{subtype}|{segment} yields ">A/New York/3197/2009||" with a 200.

So presets ship per data type, and the influenza one for genome_feature
waits on an API-side addition to GENOME_METADATA_FIELDS. The equivalent
on genome_sequence works now, since segment and accession are native
there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All seven virology fields resolve (genome_feature enrichment 35 -> 42),
and the flu-A preset renders byte-for-byte as predicted:

  >A/New York/3197/2009|A|N1|2009-04-29||USA|Human|CY086966

Nine presets with real output, a field reference with observed values,
and five findings that constrain the UI. Two earlier gaps are closed:
brace escaping now round-trips, and genome_sequence FASTA enriches
(verified with two enriched-only fields, so it is a real join, not
native-field luck).

The unknown-field divergence persists -- {hostname} still renders empty
with a 200 -- which is why validating templates against /fields is
load-bearing rather than cosmetic.

Three data traps worth knowing before designing presets: subtype holds
"Influenza B" for type-B records (hence separate A/B presets), strain is
empty on ~2% of influenza genomes and leads the GISAID format, and an
absent value yields "[collection_date=]" in NCBI modifier form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The virology fields, genome_sequence FASTA enrichment and brace escaping
all landed, so three blockers and one caution are gone. The presets
section is replaced with the seven verified format strings and the four
data facts that constrain them (subtype carries "Influenza B" for type-B
records; strain is empty on ~2% of flu genomes and leads the GISAID
format; an absent value yields "[collection_date=]"; patric_id contains
a pipe).

One divergence survives: an unknown field still renders empty with a
200, which keeps client-side /fields validation load-bearing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The wizard preview joined description fields with ' | ' while the server
joined them with a space, because the client never sent a delimiter and
the server's default was ' '. The server now accepts
http_fasta_description_delimiter, so send it -- and derive the preview
from the same values rather than a second hardcoded string.

  before:  >fig|1545742.911.peg.9| MGYG-HGUT-00869 Human USA
  after:   >fig|1545742.911.peg.9| MGYG-HGUT-00869 | Human | USA

The ID delimiter is sent too, at the server's own default of '|', so
nothing changes for anyone who does not touch it.

Three places built FASTA header parameters: the form-POST path, the xhr
path, and the in-browser formatter, each with its own copy of the
delimiters. The first two are now one buildFastaParams(); the third
reads the configured values instead of hardcoding '|' and ' '. That
duplication is how the preview drifted from the download in the first
place.

An absent delimiter is omitted (leave the server default alone) while an
empty one is transmitted, since "" is a meaningful delimiter -- hence
the undefined checks rather than falsiness.

Tests cover both request paths and assert they agree. Verified against
alpha: the rendered header now matches the preview exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@olsonanl
olsonanl merged commit 69986f5 into BV-BRC:alpha Oct 30, 2026
@olsonanl
olsonanl deleted the feature/fasta-header-formats branch November 2, 2026 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant