U of I × MindRouter App Gallery

Sequence Studio

Scientific visualization Built with OpenCode v1.18.21 Critic status: shipped

Sequence Studio

DNA and protein sequence workbench

Paste or drop a FASTA file and instantly get GC-content landscapes, open reading frames in all six frames, restriction maps, codon usage and translation — everything a quick sequence check needs, entirely in the browser.

Sequence Studio running on a desktop screen, dna and protein sequence workbench Desktop · click to enlarge
Sequence Studio running on a phone, dna and protein sequence workbench
Phone · click to enlarge

What it is ✎ blurb written by the AI critic

Sequence Studio analyses DNA sequences in the browser: paste, pick or drop a FASTA file, or start from bundled NCBI records for phage lambda, human insulin mRNA and the pEGFP-N1 plasmid. It draws a circular or linear map with six-frame ORFs and restriction sites, sliding-window GC content and GC skew, and a codon-usage heatmap for any ORF. It can also translate a region, reverse-complement it, search for IUPAC motifs on both strands, and export ORFs as FASTA and results as CSV.

Try this

  1. Open the phage lambda record, drag the minimum ORF length slider to 300 aa, then click the longest gold bar (gene J) and follow the Codon usage link.
  2. On the pEGFP-N1 plasmid, open Codon usage and pick the egfp ORF to see its GC-rich codon choices (CTG, GGC, AAG), then turn restriction enzymes on and off on the map.
  3. In Tools, search the insulin mRNA for the motif CTGCAG or the polyA signal AATAAA, or paste your own multi-record FASTA to compare records.

Highlights ✎ written by the AI critic

  • Six-frame ORF finder with a minimum-length slider; ORFs are matched to the GenBank-annotated genes (e.g. lambda's J, H, B, cI)
  • Circular plasmid map and linear genome map with toggleable restriction enzymes (EcoRI, BamHI, HindIII, NotI, XhoI, SmaI, KpnI, PstI)
  • Sliding-window GC content and GC skew charts with an adjustable window and hover readout
  • Codon-usage heatmap and amino-acid composition chart for any selected ORF
  • Bundled NCBI records (NC_001416, NM_000207, U55762) with accession links; values checked against an independent calculation

Who it's for

Molecular biology labs, genomics courses, IBEST / GBRC users

Features

  • FASTA parser with multiple records, validation and a record switcher
  • Circular/linear sequence map with annotated ORFs (six frames, minimum length slider) and restriction sites for a chosen enzyme set (EcoRI, BamHI, HindIII, NotI, XhoI…)
  • Sliding-window GC content and GC-skew charts with adjustable window
  • Codon-usage heatmap and amino-acid composition bar chart for a selected ORF
  • Translate any selection, reverse-complement, find motif (IUPAC codes) with highlighted hits
  • Export ORFs as FASTA and results as CSV

Data & sources

  • NCBI Nucleotide NC_001416.1, Escherichia phage lambda complete genome, 48,502 bp (FASTA + GenBank) via E-utilities efetch, retrieved 2026-09-13
  • NCBI Nucleotide NM_000207.3, Homo sapiens insulin (INS) transcript variant 1 mRNA, 465 nt (FASTA + GenBank) via E-utilities efetch, retrieved 2026-09-13
  • NCBI Nucleotide U55762.1, cloning vector pEGFP-N1 complete sequence, 4,733 bp, circular (FASTA + GenBank) via E-utilities efetch, retrieved 2026-09-13
Full data provenance ✎ written by the AI reviewer

Raw data sources — Sequence Studio

All files in this directory are unmodified response bodies from the NCBI Entrez E-utilities efetch endpoint. Nothing was filtered, reformatted or re-wrapped.

  • Publisher: National Center for Biotechnology Information (NCBI), U.S. National Library of Medicine, NIH

  • Retrieval date: 2026-09-13 (all six requests between 17:51:47 and 17:51:58 UTC)

  • Method: HTTP GET, no request body, sequential with ≥1 s between requests (NCBI allows 3 requests/s without an API key). No email, tool or api_key parameters were sent. User-Agent UIdaho-gallery-data-prep/1.0. Every request returned HTTP 200 with content-type: text/plain.

  • Endpoint docs: https://www.ncbi.nlm.nih.gov/books/NBK25499/ (efetch) and https://www.ncbi.nlm.nih.gov/books/NBK25497/ (usage policy)

License / terms (applies to every file)

NCBI's policy (https://www.ncbi.nlm.nih.gov/home/about/policies/, "Molecular Data Usage") says: "NCBI itself places no restrictions on the use or distribution of the data contained therein." It also warns that original submitters may claim patent, copyright or other rights in some data, and that NCBI cannot assess such claims. None of these three records carries a usage restriction. U55762 is a commercial vector sequence submitted by CLONTECH Laboratories. Its record text is informational only, but credit the submitter anyway. The app should link each accession to its NCBI Nucleotide page.

Validation performed on every file

  • FASTA files parsed as exactly one record each. The sequence is A/C/G/T only: no N, no IUPAC ambiguity codes, no lowercase. Lengths match the expected values.

  • GenBank files parsed as exactly one record each (one // terminator). The sequence in the ORIGIN block, with digits and spaces removed and uppercased, is identical to the matching FASTA file.

  • Coordinate check: every CDS feature was translated from the FASTA sequence (standard / bacterial code, honouring complement(...) and /codon_start) and compared with its /translation qualifier. All 73 lambda CDS matched, plus 1 INS CDS and 2 pEGFP-N1 CDS, with no mismatches. This confirms that feature coordinates are 1-based, inclusive on the forward strand. No error or HTML pages were returned.

FASTA format notes (all three .fasta files)

  • One header line starting with >, then sequence lines 70 characters wide (the last line is shorter), then a newline and one extra empty line: every file ends in \n\n. A parser must skip blank lines. Sequence is uppercase. Line endings are LF, not CRLF.

  • The header is >ACCESSION.VERSION DEFINITION, i.e. the first whitespace-delimited token is the versioned accession.

  • To build a sequence, concatenate all non-header, non-blank lines and trim whitespace. The FASTA files contain no annotation; features come from the matching .gb file.

  • Each file holds a single record. To exercise a multi-record parser and record switcher, you can concatenate the three files into one valid 3-record FASTA. Doing so is a transformation, not NCBI output. (Verifier note.)

GenBank flat-file format notes (all three .gb files)

  • Standard INSDC/GenBank flat file. The header block has LOCUS, DEFINITION, ACCESSION, VERSION, KEYWORDS, SOURCE/ORGANISM, REFERENCE… and COMMENT sections. The FEATURES table follows, then ORIGIN (the sequence), then //.

  • Feature table layout: a feature key starts at column 6 and its location at column 22. Qualifiers are lines starting at column 22 with /name="value". Long values wrap onto more lines indented 21 spaces. Rejoin wrapped /translation values with no separator; rejoin other text values with a single space.

  • Qualifier values are usually double-quoted (/gene="cI"), but three kinds are unquoted: /codon_start=1, /transl_table=11 and /citation=[78] (NC_001416 only). No value contains escaped "" quotes.

  • Locations: no join(...) or order(...) appears in any of the three files. Most locations are plain start..end, complement(start..end) or a single base (465, 37589). NC_001416.gb has exactly two partial-bound locations that a parser must accept, so strip </> before reading the number:

    • variation 21737..>21737 (/gene="orf-314", /note="b2 substitution terminating at the att site")

    • mRNA complement(<23231) (/locus_tag="lambdap30", /product="mRNA-pbl")

      All CDS, gene and exon locations in all three files are complete, with no < or >. (The earlier text said there were no partial markers in any file; that was wrong and a verifier corrected it.)

  • ORIGIN block: the ORIGIN line itself has trailing spaces. Each sequence line is a 1-based position right-aligned in 9 columns, a space, then up to six 10-base lowercase blocks separated by single spaces. Strip digits and whitespace to get the sequence. The record ends with a // line followed by one empty line (the file ends in //\n\n).

  • LOCUS line fields are name, length (bp), molecule type, topology (linear or circular), GenBank division, and last-modified date (DD-MON-YYYY).


1. NC_001416.fasta — Escherichia phage lambda, complete genome (FASTA)

  • URL: https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nuccore&id=NC_001416&rettype=fasta&retmode=text

  • Bytes: 49,254

  • Header: >NC_001416.1 Enterobacteria phage lambda, complete genome

  • Records: 1. Length: 48,502 bp (matches the expected value), linear dsDNA.

  • Composition: A 12,334 · C 11,362 · G 12,820 · T 11,986. GC = 49.86 %.

  • Dates: RefSeq version NC_001416.1, record last modified 11-JAN-2023. The underlying sequence is Sanger et al. 1982.

  • Notes: The header and DEFINITION use the older name "Enterobacteria phage lambda". The current ORGANISM line in the .gb file says "Escherichia phage Lambda" (taxonomy …Lambdavirus lambda). This is RefSeq status PROVISIONAL, derived from GenBank J02459. It is the wild-type reference sequence: it does not carry the cI857, ind1 or Sam7 mutations found in commercial "lambda DNA" (see the restriction-site note below).

  • Suggested citation: Escherichia phage lambda complete genome, NCBI RefSeq NC_001416.1 (Sanger F. et al., J. Mol. Biol. 162:729–773, 1982); https://www.ncbi.nlm.nih.gov/nuccore/NC_001416.1, retrieved 2026-09-13.

2. NC_001416.gb — Escherichia phage lambda, complete genome (annotated GenBank flat file)

  • URL: https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nuccore&id=NC_001416&rettype=gb&retmode=text

  • Bytes: 176,721

  • LOCUS: NC_001416 48502 bp DNA linear PHG 11-JAN-2023. VERSION: NC_001416.1. DBLINK: BioProject PRJNA485481.

  • Records: 1. It has 96 REFERENCE entries. References 1–95 are publications with journal years 1971–1987. Reference 96 is the "NCBI Genome Project" (CONSRTM) direct submission Submitted (01-AUG-2000); it has a date in DD-MON-YYYY format but no journal year. Reference 21 is Sanger F., Coulson A.R., Hong G.F., Hill D.F., Petersen G.B., "Nucleotide sequence of bacteriophage lambda DNA", J. Mol. Biol. 162(4):729–773 (1982), PUBMED 6221115.

  • Feature counts (key: count): source 1, gene 92, CDS 73, mRNA 23, variation 68, misc_binding 10, regulatory 7, misc_recomb 6, misc_feature 2, sig_peptide 1, mat_peptide 1, unsure 1.

  • CDS details: 73 CDS features. 47 are on the forward strand and 26 are complement(...). There are 69 distinct /gene names in the file; 4 CDS have no /gene (use /product or /locus_tag instead). Every CDS has /locus_tag (lambdap01…), /codon_start, /product, /protein_id and /translation. Well-known genes include nu1 191..736 and A 711..2636 (terminase), E 6135..7160 (major capsid), J 15505..18903 (central tail fiber), int complement(27812..28882), xis complement(28860..29078), exo complement(31348..32028), bet complement(32025..32810), N complement(35037..35438), cI complement(37227..37940) (repressor), cro 38041..38241, cII 38360..38653, O 38686..39585, P 39582..40283, Q 43886..44509, S 45186..45509 (holin), R 45493..45969 (endolysin), Rz 45966..46427. Some genes overlap, e.g. C/nu3 share a stop codon, and Rz1 lies inside Rz in another frame.

  • Qualifier keys present: bound_moiety, citation, codon_start, db_xref, function, gene, host, locus_tag, mol_type, note, organism, product, protein_id, regulatory_class, replace, transl_table, translation.

  • variation features record classic mutant alleles as single-base changes, e.g. position 37589 has /note="t in ind1; c in wild-type" and 37742 has /note="t in strain ci857s7([25]); c in wild-type". The sequence itself is wild-type.

  • Suggested citation: Escherichia phage lambda complete genome with gene annotation, NCBI RefSeq NC_001416.1 (GenBank flat file); https://www.ncbi.nlm.nih.gov/nuccore/NC_001416.1, retrieved 2026-09-13.

3. NM_000207.fasta — Human insulin (INS) mRNA, transcript variant 1 (FASTA)

  • URL: https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nuccore&id=NM_000207&rettype=fasta&retmode=text (an unversioned accession returns the latest version, which is NM_000207.3)

  • Bytes: 541

  • Header: >NM_000207.3 Homo sapiens insulin (INS), transcript variant 1, mRNA

  • Records: 1. Length: 465 nt. Shown as cDNA letters (T, not U). Composition: A 91 · C 156 · G 141 · T 77; GC = 63.87 %.

  • Dates: version .3 replaced NM_000207.2 on 2 Jun 2019. The record was last modified 09-SEP-2026.

  • Suggested citation: Homo sapiens insulin (INS), transcript variant 1, mRNA, NCBI RefSeq NM_000207.3 (MANE Select); https://www.ncbi.nlm.nih.gov/nuccore/NM_000207.3, retrieved 2026-09-13.

4. NM_000207.gb — Human insulin (INS) mRNA (annotated GenBank flat file)

  • URL: https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nuccore&id=NM_000207&rettype=gb&retmode=text

  • Bytes: 10,389

  • LOCUS: NM_000207 465 bp mRNA linear PRI 09-SEP-2026. VERSION: NM_000207.3. KEYWORDS: RefSeq; MANE Select. REVIEWED RefSeq, derived from BC005255.1 and BM510748.1. Chromosome 11, map 11p15.5, GeneID 3630, HGNC:6081, MIM 176730.

  • Records: 1. Has 10 REFERENCE entries, dated 1959–2024.

  • Features (all 1-based, inclusive, forward strand): gene INS 1..465. exon 1..42, 43..246 and 247..465. CDS 60..392: 333 nt = 110 aa + stop codon; product "insulin preproprotein", protein_id NP_000198.1, translation starts MALWMRLLPLL…. sig_peptide 60..131. proprotein 132..389 (proinsulin). mat_peptide 132..221 (insulin B chain), 228..320 (C-peptide) and 327..389 (insulin A chain). regulatory 446..451 (polyA signal AATAAA). polyA_site 465.

  • Suggested citation: Same as file 3 (NCBI RefSeq NM_000207.3, GenBank flat file).

5. U55762.fasta — Cloning vector pEGFP-N1, complete sequence (FASTA)

  • URL: https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nuccore&id=U55762&rettype=fasta&retmode=text

  • Bytes: 4,950

  • Header: >U55762.1 Cloning vector pEGFP-N1, complete sequence, enhanced green fluorescent protein (egfp) and neomycin phosphotransferase genes, complete cds

  • Records: 1. Length: 4,733 bp. Topology is circular (see the LOCUS line in the .gb file; FASTA does not carry topology). Composition: A 1,140 · C 1,285 · G 1,246 · T 1,062; GC = 53.48 %.

  • What it is: U55762 is not wild-type Aequorea victoria GFP and not a generic "pGFP". It is the complete synthetic sequence of the CLONTECH mammalian expression vector pEGFP-N1, in GenBank division SYN, taxon 47945 "Cloning vector pEGFP-N1". It encodes EGFP (enhanced GFP: a FACS-optimized, codon-humanized GFP variant; see Cormack et al. 1996) and a neomycin phosphotransferase (kanamycin/G418 resistance) gene. The two genes sit on one circular plasmid.

  • Dates: GenBank version U55762.1, submitted 17-APR-1996 by P.A. Kitts, CLONTECH Laboratories; record last modified 22-AUG-2003.

  • Suggested citation: Cloning vector pEGFP-N1, complete sequence (Kitts P.A., CLONTECH Laboratories, 1996), GenBank U55762.1; https://www.ncbi.nlm.nih.gov/nuccore/U55762.1, retrieved 2026-09-13.

6. U55762.gb — Cloning vector pEGFP-N1 (annotated GenBank flat file)

  • URL: https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nuccore&id=U55762&rettype=gb&retmode=text

  • Bytes: 9,370

  • LOCUS: CVU55762 4733 bp DNA circular SYN 22-AUG-2003. The LOCUS name is CVU55762, not the accession, so use ACCESSION/VERSION (U55762 / U55762.1) as the identifier.

  • Records: 1. Has 3 REFERENCE entries: Cormack B.P., Valdivia R.H., Falkow S., "FACS-optimized mutants of the green fluorescent protein (GFP)", Gene 173:33–38 (1996), PUBMED 8707053; Kitts P.A., unpublished; and the 1996 direct submission.

  • Features (sparse annotation, only 4): source 1..4733. gene egfp 679..1398. CDS 679..1398: 720 nt = 239 aa + stop; /gene="egfp", product "enhanced green fluorescent protein", protein_id AAB02574.1, /transl_table=11, translation MVSKGEELFTGVVPILVELDGDVNGHKF…. CDS 2629..3423: 795 nt = 264 aa + stop; product "neomycin phosphotransferase", protein_id AAB02575.1, no /gene qualifier. The record does not annotate the promoter, polyA signal, replication origins or MCS. Do not invent coordinates for those; if the app shows them, label them as not from this record.

  • Suggested citation: Same as file 5 (GenBank U55762.1, GenBank flat file).


DERIVED reference values (not downloaded; for testing the app only)

These values were computed by the data-prep agent from the raw FASTA files above using Python 3's standard library. They are not NCBI data and are not stored as files. Use them to sanity-check the app's restriction-site finder.

Method: Concatenate the FASTA sequence lines and uppercase them. For each recognition site, record every 1-based start position i where seq[i-1 : i-1+len(site)] == site. Overlapping matches are allowed. Only the forward strand is searched, which is sufficient because all of these sites are palindromic. The search is linear only: no wrap-around across position 1, even for circular U55762. Positions are the first base of the recognition sequence, not the cut position.

Enzyme (site)NC_001416 (48,502 bp)NM_000207 (465 nt)U55762 (4,733 bp, circular)
EcoRI (GAATTC)5: 21226, 26104, 31747, 39168, 4497201: 629
BamHI (GGATCC)5: 5505, 22346, 27972, 34499, 4173201: 660
HindIII (AAGCTT)6: 23130, 25157, 27479, 36895, 37459, 4414101: 622
NotI (GCGGCCGC)001: 1400
XhoI (CTCGAG)1: 3349801: 613
SmaI (CCCGGG)3: 19397, 31617, 3988801: 656
KpnI (GGTACC)2: 17053, 1855601: 645
PstI (CTGCAG)283: 240, 288, 3151: 634

HindIII in lambda: Textbooks and suppliers say lambda has 7 HindIII sites, but that is for the commercial strain cI857ind1 Sam7. The ind1 mutation (position 37589, c→t; annotated as a variation feature in NC_001416.gb) turns the wild-type AAGCTC at 37584..37589 into an extra AAGCTT site. The wild-type RefSeq sequence bundled here therefore correctly has 6. An app reporting 6 for NC_001416 is correct, not a bug.

Derived CDS lengths: CDS 60..392 in NM_000207 is 333 nt (110 aa + stop). EGFP 679..1398 in U55762 is 720 nt (239 aa + stop). All 76 CDS translations across the three .gb files match their /translation qualifiers, as described under Validation above.

How it was built

Written by OpenCode 1.18.21 (opencode.ai) on qwen/qwen3.8-27b, served by MindRouter, the University of Idaho's on-premises AI platform.

16harness rounds
135.1minutes of harness work (2.3 h)
2,810lines of code
229KB, single-file app

Rounds by kind

build1
feedback8
continue4
critic3

Lines of code per file

body html283
app css399
app js1,872
scripts256

Critic scores ✎ scored by the independent AI critic, 1–10

visual 8 out of 10
usefulness 8 out of 10
correctness 8 out of 10
interactivity 8 out of 10

Independent audits ✎ by the AI critic

Data honesty: checked against raw data Authorship audit: every app-code commit is a harness round

Automated checks

Console no console errors No network 0 external requests Phone layout 0 px overflow Canvases no empty canvases Brand header header present No key leak no key in source

Checked by critic-verify.

Data spot checks ✎ written by the AI critic

  • Lambda NC_001416 GC content: UI 49.86% vs raw FASTA (12,820 G + 11,362 C) / 48,502 = 49.858% - match
  • NM_000207 GC 63.87% and U55762 GC 53.48% vs raw 63.871% / 53.476% - match
  • Lambda HindIII: UI 6 sites (23130, 25157, 27479, 36895, 37459, 44141) vs Python search of raw FASTA - identical (wild-type RefSeq, see SOURCES.md)
  • Lambda ORFs >=100 aa: UI 67 (per-frame 9+14+14+13+5+12) vs independent six-frame scan 67; >=300 aa: 12 vs 12; longest +1 15,505-18,900 1,132 aa labelled J (GenBank J 15505..18903 incl. stop) - match
  • EGFP (U55762 679..1398): UI translation MVSKGEELF...GMDELYK* identical to Python translation; codon counts AAG 19, GGC 19, CTG 18, GAC 16 and aa counts G 22, L 21, K 20 identical
Show all 9 spot checks
  • Insulin NM_000207: UI ORF +3 60..389 110 aa matched to INS; protein MALWMRLL...LENYCN identical to Python translation of CDS 60..392
  • GC CSV lambda window 500 at position 1: 49.80% / skew 0.1325 vs Python 49.8 / 0.1325; hover at 19,981 bp GC 61.4% vs Python 61.4%
  • Reverse complement U55762 1..20: UI TTGATTACTATTAATAACTA vs Python - identical
  • Motif GGNCC in U55762 after round 14: UI 13 sites vs Python 13 distinct sites (26 strand hits); GAATTC: 1 site at 629 (both strands)
Build notes ✎ written by the AI reviewer

All app code (src/, scripts/, data/embed/) was written by OpenCode 1.18.21 running default-agent (qwen/qwen3.8-27b) through MindRouter. The driver wrote only FEEDBACK.md and its own test scripts. The tamper guard never fired. Data prep reported READY; the driver re-validated every raw file (lengths, alphabet, GenBank ORIGIN matching the FASTA, no error pages), so no data commit was needed.

  • Rounds: 13 total = 1 build + 8 feedback + 4 continue, 7,118 harness seconds. Round 1 (build, 977 s) produced a working shell with serious bugs: user FASTA crashed, ORFs were duplicated across frames, reverse ORFs were broken, the GC skew formula was wrong, topology was mis-parsed, and the codon heatmap overflowed the page.

  • Rounds 2–4 got a 15-item and then an 8-item FEEDBACK.md and produced no app changes. In round 2 the model's output degenerated into repeated '!!!!' tokens after 313 s. Round 3 looped paging through the 200 KB generated index.html for 27 minutes and was killed. Round 4 re-ran the same Python check about 25 times and was killed.

  • Round 6 (a 6-item batch with redesign work) ran the same grep 72 times and was killed.

  • What worked: small batches of 3–5 precisely located items (function names, exact symptoms and measured values), plus explicit instructions not to re-verify, not to open index.html and not to cat the large .gb files. Every such round finished in 86–267 s with real fixes (rounds 5, 7–13).

  • Round 8's DEFINITION fix regressed by pulling the whole GenBank file into the description (49,814 chars, causing page overflow); round 9 fixed it.

  • Other apps' rounds across harnesses also showed many kills at the same time, which suggests heavy load on the shared backend. There were no network errors in any round.

The 8-feedback-round limit was reached (rounds 2, 6, 8, 9, 10, 11, 12, 13, with 3–5 and 7 as re-runs), so the minor flaws listed above were not sent back. Final checks: the verifier passes clean and the driver's Playwright checks cover every SPEC feature (paste, file picker, drop, record switch, enzyme toggles, both sliders, map arc clicks, GC hover, codon grid, translate ±frames, reverse complement, IUPAC motif, all downloads) at 1440 px and 390 px with no errors. FEEDBACK.md was deleted and committed as 9421498. No servers or browsers from this driver were left running.

Round-by-round timeline

Every harness round, in order. The prompts and FEEDBACK.md were written by the AI reviewer; the app code was written by the harness. Expand a round to see the exact prompt and the FEEDBACK.md the harness received.

    1 build 16.3 min · 6 files changed, 4008 insertions(+), 3 deletions(-) Show prompt

    Prompt given to the harness:

    Read AGENTS.md, SPEC.md and BRAND.md in this directory. Then build the complete app they describe, step by step: first write scripts in scripts/ that turn the raw data in data/raw/ (see data/raw/SOURCES.md — three NCBI example sequences as FASTA plus matching GenBank files with annotated CDS features) into compact JSON in data/embed/ and run them; then write src/body.html, src/app.css and src/app.js; run python3 build.py --check and fix any error it reports. Implement every required feature in SPEC.md with a polished, modern, responsive University of Idaho design. Do not stop until python3 build.py --check succeeds and every feature is implemented.
    

    No feedback file was sent in this round.

    3 continue 27.8 min · — Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.
    

    No feedback file was sent in this round.

    4 continue 25.9 min · 1 file changed, 62 insertions(+), 106 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.
    

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer feedback — batch 1 of 2 (tested in Chromium at 1440×900 and 390×844)

    Working notes (please read first)
    • Edit only src/app.js, src/body.html, src/app.css and scripts/build_data.py (then re-run the script).

    • Do not open index.html — it is generated by build.py (200 KB) and just duplicates src/.

    • Do not read the .gb files in full (NC_001416.gb is 176 KB). Look at them with head -12 data/raw/U55762.gb or grep -n on a keyword; data/raw/SOURCES.md already documents their format.

    • Work through the items in order, rebuilding (python3 build.py --check) after each one.

    • This batch is the core correctness bugs. The sequence-map redesign, motif highlighting and more CSV exports will be in the next batch — do not start those now.

    Items
    1. The user's own FASTA crashes (critical). Paste this valid FASTA and click "Parse & load" (same with the file picker and with drag-and-drop): the status line shows Cannot read properties of undefined (reading 'indexOf') and the console shows that uncaught error; the panels do not render. Pasted/uploaded records are built with a seq field, while the map, restriction, GC and tool code read r.sequence (only the bundled examples have it). Expected: user records get the same shape as bundled records, status "Loaded 2 records from pasted input", and every tab works for them.

       >test_one Synthetic test record one
       ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCGGGCTGAGAATTCGGATCCAAGCTTTTTT
       >test_two Second synthetic record
       GGGCCCTTTAAACCCGGGATGCCCAAAGGGTTTCTCGAGGCGGCCGCTTGACCCATGGATCCTAGGTACCCTGCAGTAA
    1. ORFs are duplicated across frames. Lambda at minimum 100 aa: tracks show "+1 30 ORFs", "+2 30 ORFs", "+3 30 ORFs" and the table lists the same ORF three times (+1 16,009–18,900 964 aa, +2 16,009–18,900 964 aa, +3 16,009–18,900 964 aa). Inside each frame the scan advances one base at a time looking for ATG, so it finds starts in every frame. Expected: within a frame, step in codons (3 bases) so each ORF belongs to exactly one frame; no duplicates.

    2. Reverse-strand ORFs are broken. They come out with start > end (Frame -1 · 18129..16438). As a result their frame-track bars are 0.5 %-wide slivers, selecting one shows an empty codon heatmap and no amino-acid bars, "Export ORFs as FASTA" wrote 9 records with no sequence lines, and they never match annotated complement CDS (the embedded CDS use start < end with strand: -1, e.g. cI 37227..37940). Expected: store every ORF as low start ≤ end plus strand, so bars, codon usage, amino-acid composition, translation, FASTA export and CDS matching all work for reverse ORFs.

    3. Codon usage tab: empty on first visit, and the heatmap layout is broken. (a) Load an example and click "Codon usage" before visiting ORFs: the ORF dropdown is empty and the panel says "Select an ORF to see codon usage." ORFs are only computed when the ORFs tab renders. Expected: compute ORFs when a record loads and preselect a sensible ORF (the annotated CDS if there is one, else the longest), so heatmap, bars and translation show at once. (b) The heatmap header lists all 61 codons as one row of columns, but each amino-acid row has only 1–6 cells, so numbers sit under the wrong codon labels, and the table is ~2,000 px wide, pushing the whole page sideways at 1440 px (cards are cut off at the left edge). Expected: a compact grid that fits inside its card at desktop and at 390 px, where each cell shows its own codon name and count (e.g. the standard genetic-code layout: 4 blocks by first base, 4×4 cells, or one row per amino acid with the codon printed inside each cell), a color-scale legend, and a hover tooltip (codon, amino acid, count, per 1,000 codons).

    4. Record metadata is wrong (data script). Summary card "Topology" shows DNA for lambda and pEGFP-N1 and mRNA for insulin. data/embed/sequences.json has "topology":"DNA", "moltype":"bp", "date":"PHG", "organism":null, and U55762's definition is cut off at "…complete sequence, enhanced green". The LOCUS line split on whitespace is LOCUS name length bp moltype topology division date (e.g. LOCUS CVU55762 4733 bp DNA circular SYN 22-AUG-2003), and DEFINITION / ORGANISM continuation lines are indented 12 spaces. Expected: NC_001416 linear, NM_000207 linear, U55762 circular; molecule type, division and date in the right fields; organism filled; full definition. The circular map must then actually be drawn for U55762.

    5. GC skew uses the wrong formula. Chart and note use (G−T)/(G+T); GC skew is (G−C)/(G+C). Fix both. Also the x-axis title "position (bp)" is clipped at the bottom edge of the GC charts.

    6. "Clear" leaves stale UI; sources are hidden or wrong. After "Clear" the record chip (e.g. "NC_001416 48,502") stays in the Sequence header, and the whole "Data & sources" section disappears (it is inside the hidden analysis block). After pasting test_one, Data & sources lists "test_one … NCBI Nucleotide" linking to https://www.ncbi.nlm.nih.gov/nuccore/test_one. Expected: chips removed on Clear; "Data & sources" always visible and always listing the three bundled NCBI records (NC_001416.1, NM_000207.3, U55762.1) with links, publisher (NCBI) and the 2026-09-13 retrieval date; user-supplied records described as "your sequence, analysed locally" with no NCBI link.

    7. First visit shows an empty page. Nothing is analysed until the visitor clicks an example. Expected: auto-load the three bundled examples on first visit as one multi-record set with U55762 (pEGFP-N1) active, so the record switcher shows three chips and every panel has content immediately; keep the example buttons in the empty state that appears after "Clear", plus a "Load all 3 examples" button there.

    5 continue 3.2 min · 4 files changed, 89 insertions(+), 89 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.
    

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer feedback — batch 1 (five focused bugs)

    How to work on this (please follow)
    • Every diagnosis below was already confirmed by the reviewer in a real browser. Do not re-verify with scripts. Open src/app.js, make the change, run python3 build.py --check, and move to the next item.

    • If you catch yourself running the same check twice, stop checking and edit the code.

    • Only edit src/app.js (and src/body.html / src/app.css if needed). Do not open index.html (generated, 200 KB) or the .gb files in data/raw/.

    • More items (map redesign, motif highlighting, CSV exports, data-script metadata) will come in a later batch — not now.

    Items
    1. The user's own FASTA crashes. Paste a valid FASTA, click "Parse & load" (same via the file picker and via drag-and-drop): the status line shows Cannot read properties of undefined (reading 'indexOf') and panels do not render. Cause: parseFasta builds records with a seq field, but renderMap, renderRestrictionTable, renderGCCharts, renderORFs, the tools and the exports all read r.sequence (only the bundled examples have it). Expected: pasted, picked and dropped records get a sequence field (plus length, composition, cds: [], topology: "linear") so every tab works for them.

    2. ORFs are duplicated across frames. Lambda at min 100 aa: tracks "+1 30 ORFs", "+2 30 ORFs", "+3 30 ORFs" and the table lists the same ORF as +1, +2 and +3 with identical coordinates (16,009–18,900, 964 aa). Cause: in findORFs the while loop does i++ when the codon is not ATG, so it looks for ATG at every base of each shifted string. Expected: within a frame, advance i += 3 (both when there is no ATG and after an ORF ends, continue from the codon after the stop), so each ORF appears in exactly one frame.

    3. Reverse-strand ORFs have start > end. They are reported as e.g. Frame -1 · 18129..16438. Consequences: frame-track bars are 0.5 %-wide slivers (negative width), codonUsage slices an empty string (empty heatmap, no amino-acid bars), "Export ORFs as FASTA" writes records with no sequence lines, and matchCds never matches the embedded complement CDS (which are stored as start < end with strand: -1). Expected: in findORFs store start = Math.min(...) and end = Math.max(...) (1-based, inclusive) plus strand, so bars, codon usage, translation, FASTA export and CDS matching all work for reverse ORFs (e.g. lambda cI complement(37227..37940) should show "cI" in the Match column at min 30 aa).

    4. Codon usage tab is empty on first visit. Load an example and click "Codon usage" before opening the ORFs tab: the ORF dropdown is empty and the panel says "Select an ORF to see codon usage." Cause: ORFs are only computed inside renderORFs, which only runs when the ORFs tab is shown, and renderAll resets selectedOrf to −1. Expected: compute state.orfs whenever a record is loaded/activated or the min-length changes, and preselect a sensible ORF (the one matching an annotated CDS if any, else the longest), so the codon heatmap, amino-acid chart and translation are filled immediately.

    5. GC skew formula is wrong. gcProfile computes (g - t) / (g + t) and the GC panel note says (G−T)/(G+T). GC skew is (G − C)/(G + C). Fix the computation and the note in src/body.html.

    7 continue 4.2 min · 4 files changed, 127 insertions(+), 92 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.
    

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer feedback — batch 2a (three small fixes in src/app.js and src/body.html)

    Batch 1 is fixed and verified in the browser (user FASTA loads, ORFs no longer duplicated, reverse ORFs correct, GC skew formula). Keep all of that.

    How to work on this (please follow)
    • The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS.

    • Only src/app.js and src/body.html need changes for this batch. No CSS work is needed.

    • Make one change, run python3 build.py --check, then go to the next item. Three items, then stop.

    • If you notice you are running the same command twice, stop and edit instead.

    Items
    1. Codon usage dropdown is empty on first visit. Load an example and click "Codon usage" before "ORFs": the select #ss-codon-orf has 0 options and the panel says "Select an ORF to see codon usage." In src/app.js, computeOrfs() sets state.orfs/state.selectedOrf, but the options of both selects are only created by renderOrfSelect(), and that is only called at the end of renderORFs(). Fix: call renderOrfSelect() at the end of computeOrfs().

    2. "Clear" leaves a stale chip; Data & sources disappears or shows fake links. (a) In the ss-clear-btn click handler, also call renderRecordSwitcher() (or empty #ss-record-switch), because the old record chip stays visible after Clear. (b) In src/body.html the <section class="ss-sources"> is inside <div id="ss-analysis" hidden>; move that whole section to after the closing </div> of #ss-analysis so it is always visible. (c) renderSources() should always list the three bundled records from getEmbedded() (accession.version, name, length, link https://www.ncbi.nlm.nih.gov/nuccore/ + version, "NCBI Nucleotide · retrieved 2026-09-13"). Loaded records that are not bundled (pasted, picked or dropped) go in a separate line such as "Your sequence test_one — analysed locally in your browser, not uploaded", with no NCBI link. Call renderSources() once in init() too.

    3. The page starts empty. In init(), instead of ending with showAnalysis(false), load all three bundled examples as one set: loadRecords(getEmbedded(), "bundled examples"), then setActive(2) so U55762 (pEGFP-N1) is the active record. In renderExamples() add a first button "Load all 3 examples" that does the same, so the empty state shown after "Clear" offers it.

    14 critic 1.1 min · 4 files changed, 120 insertions(+), 60 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer feedback — critic batch 1 (five correctness items)

    Verified in a real browser and checked against an independent Python calculation from data/raw: restriction-site counts and positions, ORF counts (lambda: 67 ORFs at min 100 aa, 12 at min 300), EGFP codon counts and amino-acid composition, translation of U55762 679..1398, reverse complement, GC window values and all CSV downloads are correct. Keep all of that.

    How to work on this (please follow)
    • Each diagnosis below was already confirmed in a browser. Do not re-verify, do not grep the CSS repeatedly.

    • All five items are in src/app.js (item 3 is in src/body.html). Make one change, run python3 build.py --check, go to the next item. Five items, then stop.

    • If you notice you are running the same command twice, stop and edit instead. Do not open index.html. Do not cat the .gb files.

    Items
    1. Motif search highlights the wrong bases on reverse-strand hits (toolMotif()). For a reverse-strand hit, h.pos is already the leftmost forward-strand coordinate (n - (m.index + motif.length - 1)), but the display and the CSV use seq.slice(pos - motif.length, pos), which is shifted left by motif.length - 1. Example: insulin (NM_000207), motif CTGCAG, the "−" hit at 240 highlights AGGACC instead of CTGCAG; pEGFP-N1 (U55762), motif GAATTC, the "−" hit at 629 highlights GCTTCG. Fix: for both strands the matched forward region is seq.slice(pos - 1, pos - 1 + motif.length) with left context seq.slice(max(0,pos-6), pos-1) and right context seq.slice(pos-1+motif.length, pos+motif.length+4). In the CSV, write matched_sequence as the actual motif-strand sequence: the forward slice for "+", and reverseComplement(forward slice) for "−".

    2. Palindromic motifs are counted twice, and overlapping hits are missed (toolMotif()). GAATTC in U55762 reports "2 hits" for its single EcoRI site (one "+" and one "−" at 629); CTGCAG in NM_000207 reports 6 hits for 3 sites (240, 288, 315). Fix: after collecting both strands, if a "−" hit has the same pos as a "+" hit, keep one row and mark its strand as "±" (both strands); the count line should then say "1 site" / "3 sites". Also the note says "Overlapping hits are reported", but the while (regex.exec(...)) loop resumes after the whole match, so overlapping hits are skipped (e.g. GGNCC in U55762 gives 24 rows, but there are 26 strand hits = 13 + 13). Set regex.lastIndex = m.index + 1 after every match on both strands so overlaps are found.

    3. The motif placeholder suggests syntax the search rejects. #ss-motif in src/body.html has placeholder="e.g. GAATTC or A[CG]T", but typing A[CG]T gives 'Invalid character "[" at position 2'. Change the placeholder to e.g. GAATTC or GGNCC (IUPAC letters only).

    4. Partial ORFs show fractional lengths (findORFs()). When no stop codon is found, end = s.seq.length, so aaLen = (end - start) / 3 can be non-integer: NM_000207 at minimum 30 aa lists "+2 · 341 · 465 · 41.667 aa · partial". Fix: for an ORF without a stop, trim end to the last complete codon (end = start + 3 * Math.floor((s.seq.length - start) / 3)) before computing aaLen and the coordinates, so the length is an integer (41) and the End column is the last base of the last full codon.

    5. One bad record makes the whole paste fail with a misleading message (parseFasta()). Pasting three valid records plus >bad digits / ACGT123XYZ loads nothing and says "Record 'bad' looks like a protein sequence". The same happens for >bad2 / ACGTXXJJACGT. Two fixes: (a) When building cur.seq, strip digits as well as whitespace (line.replace(/[\s\d]+/g, "")) — numbered sequence lines are common. (b) Do not throw when looksLikeProtein(seq) is true; instead skipped.push({ id: r.id, reason: "looks like a protein sequence" }) and continue, so the valid records still load and the status says e.g. "Loaded 3 of 4 records — skipped 'bad': invalid characters X, Y, Z at positions 5–7". Only when no record is valid, show the error (for a single protein record keep the current wording "Record 'prot' looks like a protein sequence — Sequence Studio analyses DNA/RNA.").

    15 critic 4.5 min · 5 files changed, 211 insertions(+), 78 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer feedback — critic batch 2 (four visual / interaction items)

    Batch 1 is verified fixed in the browser: reverse-strand motif highlights, palindromic sites counted once ("±"), overlapping hits, placeholder, integer partial-ORF lengths, and mixed pastes that load the valid records and skip the bad one. Keep all of that, and keep all numbers exactly as they are (they match an independent calculation).

    How to work on this (please follow)
    • Each diagnosis below was already confirmed in a browser. Do not re-verify, do not grep the CSS repeatedly.

    • Items 1 and 3 are in renderMap() / drawLineChart() in src/app.js; item 2 is one CSS rule in src/app.css; item 4 is in renderMap() plus a small element in src/body.html. Make one change, run python3 build.py --check, go to the next item. Four items, then stop.

    • If you notice you are running the same command twice, stop and edit instead. Do not open index.html. Do not cat the .gb files.

    Items
    1. The circular map (pEGFP-N1, U55762) is small and its labels are unreadable. On a 1440 px screen the map card is 1272×675 px but the SVG is capped at maxWidth = "760px" with a 760×600 viewBox, so the ring is only ~360 px across with large empty margins, and every label (enzyme names, tick labels "500/1k…", frame labels "+1…−3", the "egfp" / "neomycin phosphotr…" gene labels) is drawn at font-size 8–9 → 8–9 px on screen. On a 390 px phone the same SVG shrinks to ~330 px so those labels are about 4 px. Fix in renderMap() (circular branch only): raise the SVG max width to 1000px; tighten the viewBox to the drawn content (about -150 50 820 430 — the ring is centred at 260,260 with outer labels reaching ±250 px horizontally and ±200 px vertically); raise label font sizes to 12 (enzyme and gene labels), 11 (bp ticks) and 10 (frame labels), with the label column spacing (prevY + 13) raised to prevY + 16; and show the full product name "neomycin phosphotransferase" (do not truncate at 18 chars). At viewport widths ≤ 700 px give the circular SVG min-width: 640px inside the existing horizontally scrollable #ss-map so text stays ≥ 10 px on screen.

    2. "Swipe sideways to explore the map →" shows above the circular map on phones. renderMap() correctly sets swipeNote.hidden = isCircular, but the phone media query in src/app.css has .ss-map__swipe-note { display: block; }, which overrides the hidden attribute. Change that rule to .ss-map__swipe-note:not([hidden]) { display: block; }. (If item 1 makes the circular map scrollable on phones, set the note visible for circular maps too — but only when the SVG is actually wider than #ss-map.)

    3. GC charts: the x-axis title is clipped and the mean label sits on the data. In drawLineChart() the "position (bp)" text is placed at y: H - 0, so its descenders are cut off at the bottom of the SVG (visible at 1440 px and at 390 px), and it collides with the tick labels at H - 8. Increase the bottom padding (padB 32 → 46 on wide, 40 → 52 on narrow), draw the tick labels at padT + plotH + 16 and the axis title at padT + plotH + 34. For the "mean 49.9 %" label: place it at the right end of the dashed line (x: W - padR - 4, text-anchor: end) and 6 px above it, on a small white rounded rect behind the text, so it no longer overprints the green line at the left edge.

    4. The map gives almost no feedback when you hover or click. ORF bars/arcs, CDS bars/arcs and restriction ticks only have native <title> tooltips (which take ~1 s to appear), there is no hover state, and clicking an ORF only draws a black outline — nothing tells the visitor that the Codon usage and Tools tabs now use that ORF. Add: (a) a CSS hover state for the clickable ORF marks (#ss-map svg [cursor=pointer]:hover { opacity: .75; } plus transition: opacity .15s); (b) a single custom tooltip <div class="ss-map-tip"> positioned near the pointer on mousemove over any ORF, CDS or restriction mark, showing the same text as its <title> (e.g. "Frame +1 · 15,505–18,900 · 1,132 aa", "egfp 679..1398", "EcoRI at 629"), hidden on mouseleave; (c) a one-line status under the map, <p id="ss-map-selected" class="ss-note">, updated on ORF click and on first render: "Selected ORF: Frame +1 · 15,505–18,900 · 1,132 aa · J — see Codon usage and Tools", where "Codon usage" is a link-styled button that calls switchTab("codon").

    16 critic 10.9 min · 3 files changed, 50 insertions(+), 53 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser. Fix every item, keeping everything that already works, then run python3 build.py --check.

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer feedback — critic batch 3 (five small items, final)

    Batch 2 is verified in the browser: the circular map is now 1000 px wide with 12–15 px labels, the swipe-note rule respects hidden, the GC axis title and the right-hand mean label are clean, the map tooltip works on arcs and restriction ticks, and the "Selected ORF" line with the Codon usage button appears. Keep all of that, and keep every number exactly as it is.

    How to work on this (please follow)
    • Each diagnosis below was already confirmed in a browser. Do not re-verify, do not grep the CSS repeatedly.

    • Make one change, run python3 build.py --check, go to the next item. Five items, then stop.

    • If you notice you are running the same command twice, stop and edit instead. Do not open index.html. Do not cat the .gb files.

    Items
    1. Every unmatched ORF is labelled "J" (updateMapSelected() in src/app.js). The line var name = cds ? (cds.gene || cds.product || "CDS") : "J"; hard-codes "J" (it was only an example in the last feedback). For an ORF with no annotated CDS match, e.g. lambda frame −2 16,438–18,129, the status line wrongly says "· J". Fix: when cds is null, leave the name out entirely: "Selected ORF: Frame −2 · 16,438–18,129 · 564 aa — see Codon usage and Tools".

    2. The left-hand gene label on the circular map is cut off (renderMap(), circular branch). With viewBox "-150 50 820 430", the label "neomycin phosphotransferase" (text-anchor end at x = cx − (R + 70) = 10, font-size 12) starts 43 screen px left of the SVG edge, so it reads "cin phosphotransferase" at 1440 px and on phones. Fix: widen the viewBox on the left, e.g. "-230 50 980 430" (keep the right side unchanged), so the full label fits.

    3. Phone: the circular map opens showing only its left half (renderMap()). At 390 px the circular SVG is 640 px wide inside #ss-map (clientWidth 320, scrollWidth 640) but scrollLeft stays 0, so the ring is cut off on the right, and the swipe note is hidden because swipeNote.hidden = isCircular. Fix: after wrap.appendChild(svg), set wrap.scrollLeft = Math.max(0, (wrap.scrollWidth - wrap.clientWidth) / 2) for circular maps (keep 0 for linear maps), and set swipeNote.hidden = !(wrap.scrollWidth > wrap.clientWidth + 4) for both map types (so it shows only when the map really scrolls).

    4. The ORF table's Match column says "complete" or "partial" for ORFs that match nothing (renderORFs()). That makes "complete" look like a gene name. Fix: in the Match cell show the gene/product when matchCds() finds one, otherwise an em dash "—"; for ORFs with no stop codon (o.complete === false) append a small muted tag " partial" after the length in the Length (aa) cell instead.

    5. Genetic-code wording (src/body.html, Data & sources section). "Motif search, translation and ORF prediction use the standard genetic code (table 11)" — NCBI table 11 is the bacterial/plastid code, not the standard code. Change it to "…use the standard genetic code (NCBI translation table 1; table 11 has the same codon assignments)."

What the agent was asked

The full specification the harness received, rendered from the original Markdown.

Read the specification

Sequence Studio — DNA and protein sequence workbench

Category: Scientific visualization Who it is for: Molecular biology labs, genomics courses, IBEST / GBRC users

What it should do

Paste or drop a FASTA file and instantly get GC-content landscapes, open reading frames in all six frames, restriction maps, codon usage and translation — everything a quick sequence check needs, entirely in the browser.

Data

Works on the user's own FASTA (paste, file picker, drag-and-drop). Bundle 2–3 real public example sequences with their accession numbers, e.g. the Escherichia phage lambda genome (NC_001416, 48,502 bp) and a well-known gene such as human insulin mRNA (NM_000207) or GFP (U55762), fetched at build time from NCBI E-utilities (efetch, rettype=fasta).

Raw downloads (if any) are in data/raw/ with data/raw/SOURCES.md describing where each file came from. Transform what the app needs into compact JSON in data/embed/ (write a small script in scripts/ to do it, so the transformation is reproducible). Every dataset the app shows must be credited in a visible "Data & sources" section of the app, with links. If a dataset is illustrative/synthetic, label it clearly as such in the UI — never present made-up numbers as real University of Idaho figures.

Required features

  • FASTA parser with multiple records, validation and a record switcher

  • Circular/linear sequence map with annotated ORFs (six frames, minimum length slider) and restriction sites for a chosen enzyme set (EcoRI, BamHI, HindIII, NotI, XhoI…)

  • Sliding-window GC content and GC-skew charts with adjustable window

  • Codon-usage heatmap and amino-acid composition bar chart for a selected ORF

  • Translate any selection, reverse-complement, find motif (IUPAC codes) with highlighted hits

  • Export ORFs as FASTA and results as CSV

Quality bar

This app is going into a University of Idaho gallery that shows faculty, staff and students what they can build with AI coding agents. It must look polished and modern (think a well-designed data product, not a homework demo), feel alive (smooth transitions, responsive hover/selection states, animated where it helps understanding), and do real, useful work — every control should change something meaningful. It must work well on a laptop and on a phone. Include a short "How to use" hint for first-time visitors and a helpful empty state wherever the user must supply data.

Known limitations

Reported by the independent critic. ✎ written by the AI critic

  • Data & sources text still says translation uses 'the standard genetic code (table 11)'; table 11 is the bacterial code (same codon assignments, so results are unaffected). Round 16's model output degenerated into '!!!!' tokens before it made this one-line edit.
  • Phone: after switching from the circular pEGFP-N1 map to lambda's linear map, the map keeps the centred scroll offset (starts near 8 kb instead of 0).
  • Phone: circular-map labels render at about 7 px and the outer enzyme/gene labels need a sideways swipe; the tab bar starts about 964 px down on a 390x844 screen.
  • Enzyme chips have coloured dots, but the map and legend draw every restriction site in near-black.
  • Predicted ORF coordinates exclude the stop codon, so they end 3 bp before the annotated CDS (the CDS match allows +/-3 bp).
  • ORFs and features crossing position 1 of a circular sequence are not handled; the GC profile is sampled to about 800 points for long sequences (stated in the UI).
  • The custom map tooltip was confirmed on circular-map arcs and restriction ticks; automated hover on linear-map ORF bars did not show it (clicking to select works).