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.
Desktop · 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
- 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.
- 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.
- 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
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.
Rounds by kind
| build | 1 |
| feedback | 8 |
| continue | 4 |
| critic | 3 |
Lines of code per file
| body html | 283 |
| app css | 399 |
| app js | 1,872 |
| scripts | 256 |
Critic scores ✎ scored by the independent AI critic, 1–10
Independent audits ✎ by the AI critic
Automated checks
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
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.
-
The user's own FASTA cannot be analysed (critical). Pasting this valid two-record FASTA and clicking "Parse & load" (and the same file through the file picker, and through drag-and-drop onto the drop zone) shows the status
Cannot read properties of undefined (reading 'indexOf')and the console logspageerror: Cannot read properties of undefined (reading 'indexOf'). The two record chips appear, but the map, restriction table and other panels do not render for them. Pasted/uploaded records apparently do not have the same fields as the bundled examples (e.g. the sequence field has a different name). Expected: status "Loaded 2 records from pasted input", and every tab (map, GC, ORFs, codon usage, tools, exports) works for user records exactly like it does for the examples. -
ORFs are assigned to the wrong frames / duplicated. For lambda (NC_001416) at minimum 100 aa the frame tracks show "+1 30 ORFs", "+2 30 ORFs", "+3 30 ORFs" and the table lists the same ORF three times:
+1 fwd 16,009 18,900 964,+2 fwd 16,009 18,900 964,+3 fwd 16,009 18,900 964(same for 10,746–13,100 and 891–2,633). The reverse frames −1/−2/−3 likewise all show 23. The scan within each frame is not stepping in codons, so it finds ATGs in every frame. Expected: each ORF belongs to exactly one frame (its start position modulo 3 on its strand), no duplicates, and the three forward (and three reverse) frames show different ORF sets. -
Reverse-strand ORFs are broken everywhere. They are reported with start > end (e.g.
Frame -1 · 18129..16438 · 564 aa). Consequences: (a) their bars in the frame tracks are 0.5 % wide slivers at the wrong position; (b) selecting one and opening Codon usage shows an empty heatmap and zero amino-acid bars; (c) "Export ORFs as FASTA" wrote 9 records that have a header line but no sequence lines; (d) they never match annotated complement CDS — lambda has 26 complement CDS (e.g.cIcomplement(37227..37940),intcomplement(27812..28882)) but the Match column never shows those gene names. Expected: store genomic coordinates as low..high plus strand, draw bars at the true span, compute codon usage / AA composition / translation / FASTA export from the reverse-complemented sequence, and match annotated complement CDS by gene name. -
Record metadata from the data script is wrong. The summary card "Topology" shows
DNAfor lambda and pEGFP-N1 andmRNAfor insulin.data/embed/sequences.jsonhas"topology":"DNA","date":"PHG","moltype":"bp","organism":null— the LOCUS line fields are read one position off. U55762's definition is truncated to "Cloning vector pEGFP-N1, complete sequence, enhanced green" because the multi-line DEFINITION isn't rejoined. Expected: NC_001416 = linear, NM_000207 = linear, U55762 = circular; organism from the ORGANISM line; full definition text; date = last-modified date. Because pEGFP-N1 is not recognized as circular, the circular map is never shown at all today. -
The sequence map is too small, hard to read and lacks ORFs. The spec asks for a "circular/linear sequence map with annotated ORFs (six frames, minimum length slider) and restriction sites".
-
The map only shows GenBank CDS; predicted six-frame ORFs are not on it, and the minimum-length slider exists only on the ORFs tab. Put six ORF frame tracks (or rings for a circular map) on the map, with a minimum-length slider in the map panel (kept in sync with the ORFs tab).
-
At 390 px wide the whole map is about 20 px tall and every label is ~3 px — unreadable. At 1440 px the axis labels (8k, 16k, 24k…) are hidden behind the CDS bars and restriction ticks.
-
The circular map (for circular records like pEGFP-N1) uses a 44-px radius inside a 120-px-tall drawing — far too small.
-
Expected: a large, legible map — a real plasmid-style circular map for circular records (ORF rings, CDS arcs with gene labels, restriction sites with enzyme-name labels around the outside, bp ticks) and a linear map for linear records (ruler, labelled CDS, six ORF tracks, labelled restriction sites), with hover tooltips (name, coordinates, length, frame) and click-to-select that feeds the codon-usage/translation views. It must be readable on a phone.
-
Restriction-enzyme colors must not reuse the CDS colors (EcoRI and "CDS forward" are both teal; NotI and "CDS reverse" are both raspberry), which makes the legend ambiguous.
-
-
Codon-usage heatmap layout is broken. At 1440 px, after selecting an ORF, the heatmap table is ~2,000 px wide and pushes the whole page sideways (cards are cut off at the left edge and the body scrolls horizontally). The header row lists all 61 codons as columns, but each amino-acid row has only 1–6 cells, so the numbers do not sit under their codon labels (row R's cells appear under GCT/GCC/GCA… headings). Expected: a compact, readable heatmap that fits inside its card at desktop and phone width, where every cell clearly names its codon — for example the classic genetic-code grid (first base × second base, third base inside each block) with the codon and amino acid printed in each cell — plus a color-scale legend and a hover tooltip (codon, amino acid, count, frequency per 1,000 codons).
-
Codon usage tab is empty on first visit. Load an example, then click "Codon usage" directly (without opening ORFs first): the ORF dropdown is empty and the panel says "Select an ORF to see codon usage." On the phone the select is a blank box and the Amino-acid composition and Translation cards are empty. Expected: ORFs are computed when a record loads and a sensible ORF (the annotated CDS if there is one, otherwise the longest ORF) is preselected, so heatmap, amino-acid bar chart and translation are populated immediately.
-
GC skew formula is wrong. The chart and note use
(G−T)/(G+T). GC skew is(G−C)/(G+C). Fix the computation and the label. Also: the x-axis title "position (bp)" is clipped at the bottom of the GC content chart; add a hover crosshair with a tooltip (position, GC %, skew); draw the whole-sequence average GC as a dashed reference line; show the cumulative GC skew (useful for finding replication origins) as an optional second line. -
Motif hits are not highlighted. Searching
CTGCAGin insulin lists "240 AGGACCTGCAGGTGGGG" — the matched bases are not visually marked (no highlight at all), and hits are not shown on any map. Expected: matched bases highlighted (gold fill) inside each context snippet, hits drawn as markers on a position track (and/or on the sequence map), search both strands with the strand shown per hit, and a "Download hits (CSV)" button. -
Only one CSV export exists. The intro says "download ORFs as FASTA or any table as CSV", but the only CSV button is "Export ORFs as CSV". Expected CSV downloads for: restriction sites (enzyme, site, position), GC/skew profile (window start, GC %, skew), codon usage for the selected ORF (codon, amino acid, count, per-1,000), and motif hits. File names should include the accession.
-
The first impression is an empty page. On first load nothing is analysed: the visitor sees the intro, the paste box and an empty-state card. For a gallery app, auto-load a bundled example on first visit (pEGFP-N1 is a good choice — it shows the circular map) while keeping the empty state after "Clear". Also add a "Load all 3 examples" option that loads them as one multi-record set, so the record switcher is demonstrated (each example button currently loads a single record, so the switcher only ever shows one chip).
-
"Clear" leaves stale UI and hides the sources. After "Clear", the record chip (e.g. "NC_001416 48,502") stays in the Sequence header, and the entire "Data & sources" section disappears (it lives inside the hidden analysis block). Expected: chips removed; "Data & sources" always visible, always listing the three bundled NCBI accessions (NC_001416.1, NM_000207.3, U55762.1) with links, publisher and the 2026-09-13 retrieval date.
-
User sequences get bogus NCBI citations. After pasting test_one/test_two, Data & sources lists "test_one — test_one · 175 bp · linear · NCBI Nucleotide" linking to https://www.ncbi.nlm.nih.gov/nuccore/test_one. User-supplied records must be labelled as the user's own sequence (analysed locally, not uploaded) with no NCBI link.
-
Validation is all-or-nothing and too strict. Pasting
>bad/ACGTXXACGTrejects the whole input with "Record 'bad' contains invalid characters (expected A/C/G/T/N)." Expected: a per-record validation summary (name, length, GC %, number and first positions of invalid characters); lowercase accepted;Uconverted toTwith a note; IUPAC ambiguity codes (R Y S W K M B D H V N) accepted with a warning; a record that looks like protein refused with a clear message; valid records still loaded when another record is invalid; digits and spaces inside sequence lines ignored. -
Translation tool and polish. "Translate a selection" offers frames 1–3 on the forward strand only — add −1/−2/−3, show the protein in blocks of 10 with position numbers, and let a click on an ORF/CDS on the map or in the ORF table fill the start/end fields. Polish: animate chart updates (lines draw in, bars grow, 150–250 ms), hover states on ORF table rows and map features, and at 390 px the restriction "Positions" column overflows the card — wrap the positions inside the card.
-
Edit only
src/app.js,src/body.html,src/app.cssandscripts/build_data.py(then re-run the script). -
Do not open
index.html— it is generated by build.py (200 KB) and just duplicatessrc/. -
Do not read the
.gbfiles in full (NC_001416.gb is 176 KB). Look at them withhead -12 data/raw/U55762.gborgrep -non a keyword;data/raw/SOURCES.mdalready 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.
-
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 aseqfield, while the map, restriction, GC and tool code readr.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. -
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. -
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 withstrand: -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. -
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).
-
Record metadata is wrong (data script). Summary card "Topology" shows
DNAfor lambda and pEGFP-N1 andmRNAfor insulin.data/embed/sequences.jsonhas"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 isLOCUS 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. -
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. -
"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.
-
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.
-
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, runpython3 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(andsrc/body.html/src/app.cssif needed). Do not openindex.html(generated, 200 KB) or the.gbfiles indata/raw/. -
More items (map redesign, motif highlighting, CSV exports, data-script metadata) will come in a later batch — not now.
-
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:parseFastabuilds records with aseqfield, butrenderMap,renderRestrictionTable,renderGCCharts,renderORFs, the tools and the exports all readr.sequence(only the bundled examples have it). Expected: pasted, picked and dropped records get asequencefield (pluslength,composition,cds: [],topology: "linear") so every tab works for them. -
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,+2and+3with identical coordinates (16,009–18,900, 964 aa). Cause: infindORFsthewhileloop doesi++when the codon is not ATG, so it looks for ATG at every base of each shifted string. Expected: within a frame, advancei += 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. -
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),codonUsageslices an empty string (empty heatmap, no amino-acid bars), "Export ORFs as FASTA" writes records with no sequence lines, andmatchCdsnever matches the embedded complement CDS (which are stored asstart < endwithstrand: -1). Expected: infindORFsstorestart = Math.min(...)andend = Math.max(...)(1-based, inclusive) plusstrand, so bars, codon usage, translation, FASTA export and CDS matching all work for reverse ORFs (e.g. lambdacIcomplement(37227..37940) should show "cI" in the Match column at min 30 aa). -
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, andrenderAllresetsselectedOrfto −1. Expected: computestate.orfswhenever 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. -
GC skew formula is wrong.
gcProfilecomputes(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 insrc/body.html. -
Every diagnosis below was confirmed by the reviewer in a real browser. Do not re-verify with scripts. Open the file, make the change, run
python3 build.py --check, move to the next item. -
If you catch yourself running the same check twice, stop checking and edit the code.
-
Edit
src/app.js,src/body.html,src/app.cssand (item 3 only)scripts/build_data.py. Do not openindex.html(generated, 200 KB) and do notcatthe.gbfiles —head -12 data/raw/U55762.gbis enough. -
The sequence-map ORF tracks, extra CSV exports and validation will come in batch 3 — not now.
-
Codon usage dropdown is still empty on first visit. Load an example, click "Codon usage" before opening "ORFs": the ORF
<select id="ss-codon-orf">has 0 options and the panel says "Select an ORF to see codon usage." Cause:computeOrfs()now setsstate.orfs/state.selectedOrf, but the<option>s of both selects are only created inrenderOrfSelect(), which is only called fromrenderORFs()(ORFs tab). Expected: callrenderOrfSelect()right aftercomputeOrfs()(load, record switch, min-length change) so the codon tab shows the preselected ORF's heatmap, amino-acid chart and translation immediately. -
Codon heatmap overflows the page and its labels are misaligned. With an ORF selected, the heatmap table makes the page wider than the window:
document.documentElement.scrollWidth − innerWidth= 952 px at 1440 px and 612 px at 390 px (the Codon usage card runs off the right edge). The header row lists all 61 codons as columns, but each amino-acid row has only 1–6 cells, so counts sit under the wrong codon names (row L's six cells sit under GCT…CGC). Expected: replace it with the standard genetic-code grid — 4 blocks for first base T/C/A/G, each block 4 rows (second base) × 4 columns (third base), or 16 compact rows — where every cell prints its codon, amino acid and count, shaded with the warm scale, plus a small color legend and a hover tooltip (codon, amino acid, count, per 1,000 codons). Stop codons shown greyed. It must fit inside its card with no page-level horizontal scroll at 1440 px and at 390 px (smaller font / wrapping blocks on the phone). -
Topology is wrong, so the circular map never appears. Summary card "Topology" shows
DNAfor NC_001416 and U55762 andmRNAfor NM_000207;data/embed/sequences.jsonhas"topology":"DNA","moltype":"bp","date":"PHG","organism":null, and U55762's definition stops at "…complete sequence, enhanced green". Inscripts/build_data.pythe 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); DEFINITION continues on following lines indented 12 spaces; the organism is the line afterORGANISMlabel (ORGANISM Escherichia phage Lambda). Expected: NC_001416 linear, NM_000207 linear, U55762 circular, correct moltype/division/date, organism filled, full definition. Re-run the script. Then check the circular branch ofrenderMap(): it draws a 44-px-radius circle inside a 900×120 viewBox, which is tiny — give circular records a square drawing (e.g. 520×520 viewBox, radius ~190) with bp tick labels around the ring, CDS arcs labelled with their gene name, and restriction sites labelled with the enzyme name outside the ring. For linear records make the viewBox taller (it is 900×120 now, so at 390 px the whole map is ~20 px tall). -
"Clear" leaves a stale record chip, and Data & sources is hidden or wrong. (a) After "Clear", the record chip (e.g. "NC_001416 48,502") is still shown next to the "Sequence" heading — clear
#ss-record-switchtoo. (b) The "Data & sources" section is inside#ss-analysis, so it disappears whenever no record is loaded — move it outside so it is always visible. (c)renderSources()lists only loaded records and builds an NCBI link for user records: after pastingtest_oneit shows "test_one … NCBI Nucleotide" linking to https://www.ncbi.nlm.nih.gov/nuccore/test_one. Expected: always list the three bundled NCBI records (NC_001416.1, NM_000207.3, U55762.1 — name, length, link to https://www.ncbi.nlm.nih.gov/nuccore/ACCESSION.VERSION, "NCBI Nucleotide, retrieved 2026-09-13"), and describe user-supplied records only as "your sequence — analysed locally, not uploaded", with no link. -
First visit shows an empty page. Nothing is analysed until the visitor clicks an example. Expected: on page load, load all three bundled examples as one multi-record set with U55762 (pEGFP-N1) active (so the record switcher shows three chips and the circular map is the first thing people see). After "Clear", the empty state appears with the three example buttons plus a "Load all 3 examples" button.
-
Motif hits are not highlighted. Searching
CTGCAGin insulin lists240 AGGACCTGCAGGTGGGG— the matched bases look exactly like the context. Expected: wrap the matched bases in a<mark>with a Pride Gold fill (black text) inside each context snippet, show a small position track (a thin bar the width of the card with a tick for each hit), and add a "Download hits (CSV)" button (columns: motif, position, matched_sequence). -
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS.
-
Only
src/app.jsandsrc/body.htmlneed 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.
-
Codon usage dropdown is empty on first visit. Load an example and click "Codon usage" before "ORFs": the select
#ss-codon-orfhas 0 options and the panel says "Select an ORF to see codon usage." Insrc/app.js,computeOrfs()setsstate.orfs/state.selectedOrf, but the options of both selects are only created byrenderOrfSelect(), and that is only called at the end ofrenderORFs(). Fix: callrenderOrfSelect()at the end ofcomputeOrfs(). -
"Clear" leaves a stale chip; Data & sources disappears or shows fake links. (a) In the
ss-clear-btnclick handler, also callrenderRecordSwitcher()(or empty#ss-record-switch), because the old record chip stays visible after Clear. (b) Insrc/body.htmlthe<section class="ss-sources">is inside<div id="ss-analysis" hidden>; move that whole section to after the closing</div>of#ss-analysisso it is always visible. (c)renderSources()should always list the three bundled records fromgetEmbedded()(accession.version, name, length, linkhttps://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. CallrenderSources()once ininit()too. -
The page starts empty. In
init(), instead of ending withshowAnalysis(false), load all three bundled examples as one set:loadRecords(getEmbedded(), "bundled examples"), thensetActive(2)so U55762 (pEGFP-N1) is the active record. InrenderExamples()add a first button "Load all 3 examples" that does the same, so the empty state shown after "Clear" offers it. -
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS repeatedly.
-
Make one change, run the command given, then go to the next item. Three items, then stop.
-
If you notice you are running the same command twice, stop and edit instead.
-
Do not open
index.html(generated) and do notcatthe.gbfiles. -
Topology is parsed wrong in
scripts/build_data.py.data/embed/sequences.jsonhas"topology":"DNA","moltype":"bp","date":"PHG"for NC_001416 and"topology":"mRNA"for NM_000207, so the Topology card is wrong and U55762 is never drawn as circular. The LOCUS line split on whitespace givesparts = ["LOCUS", name, length, "bp", moltype, topology, division, date], e.g.LOCUS CVU55762 4733 bp DNA circular SYN 22-AUG-2003→parts[4]="DNA"(moltype),parts[5]="circular"(topology),parts[6]="SYN"(division),parts[7]="22-AUG-2003"(date). Also rejoin DEFINITION continuation lines (lines starting with 12 spaces right after DEFINITION) — U55762's definition is cut at "…enhanced green"; and fillorganismfrom the text afterORGANISM. Then runpython3 scripts/build_data.pyandpython3 build.py --check. Expected result: NC_001416 linear, NM_000207 linear, U55762 circular. -
The circular map is tiny. In
renderMap()(src/app.js) the circular branch uses the sameviewBox "0 0 900 120"withR = 44, so pEGFP-N1's ring would be ~90 px across. For circular records use a squareviewBox "0 0 520 520", center 260,260,R = 180, and cap the SVG's displayed size (e.g.max-width: 520px; margin: 0 auto; display:block). Add bp tick marks with labels every 500 bp around the ring, draw CDS as thick arcs (stroke-width ~12) with the gene/product name as a text label near the arc midpoint, and draw each restriction site as a short radial tick outside the ring with the enzyme name and position as a label (e.g. "EcoRI 629"). Keep the linear branch, but make its viewBox taller (e.g.0 0 900 200) so labels don't collide with the bars. Runpython3 build.py --check. -
Motif hits are not highlighted. In
toolMotif()each hit shows a context string such asAGGACCTGCAGGTGGGGwhere the matchedCTGCAGlooks exactly like the flanking bases. Build the snippet as escaped left flank +<mark class="ss-hit">+ escaped matched bases +</mark>+ escaped right flank, and add a CSS rule.ss-hit { background: var(--ui-pride-gold); color: #191919; border-radius: 3px; padding: 0 2px; }at the end ofsrc/app.css. Also add a "Download hits (CSV)" button under the hit list (columns: motif, position, matched_sequence), using the existingdownload()helper. Runpython3 build.py --check. -
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS repeatedly.
-
Make one change, run the command given, then go to the next item. Three items, then stop.
-
If you notice you are running the same command twice, stop and edit instead.
-
Do not open
index.html(generated) and do notcatthe.gbfiles (head -8 data/raw/U55762.gbis enough). -
Regression: "definition" now contains the whole GenBank file. After the last change,
data/embed/sequences.jsonhas adefinitionof 49,814 characters for NC_001416 (4,308 for NM_000207, 1,718 for U55762): the DEFINITION rejoin inscripts/build_data.pykeeps appending every following line (COMMENT, FEATURES,/translation="…"strings…). On screen the Description card shows a wall of feature-table text ("…1020 East Meadow Circle, Palo Alto…",/gene_synonym=…), stretches the summary cards to hundreds of pixels tall, and its unbroken protein strings make the whole page scroll sideways: 106–129 px at 1440 px wide and 356–379 px at 390 px wide, on every tab. Fix: only rejoin the continuation lines that directly followDEFINITIONand start with 12 spaces; stop at the first line that does not start with a space (the next keyword,ACCESSION). Expected values:-
NC_001416: "Enterobacteria phage lambda, complete genome."
-
NM_000207: "Homo sapiens insulin (INS), transcript variant 1, mRNA."
-
U55762: "Cloning vector pEGFP-N1, complete sequence, enhanced green fluorescent protein (egfp) and neomycin phosphotransferase genes, complete cds." Also give
.ss-summary__descoverflow-wrap: anywhereand clamp it to 3 lines (display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden) with the full text in atitleattribute. Runpython3 scripts/build_data.pythenpython3 build.py --check.
-
-
Codon heatmap is 2,124 px wide and mislabelled. On the Codon usage tab
#ss-codon-heatmap's<table>is 2,124 px wide, pushing the page 952 px sideways at 1440 px and 1,782 px at 390 px. Its header lists 61 codons, while each amino-acid row has only 1–6 cells, so counts sit under the wrong codon names. ReplacerenderHeatmap()with the standard genetic-code grid built from divs (no table):-
A container
.ss-codon-tablewithdisplay:grid; grid-template-columns: 2.2em repeat(4, minmax(0, 1fr)); gap:3px; max-width:100%. -
A header row: an empty corner cell, then the second base T, C, A, G.
-
16 rows in the order first base T, C, A, G × third base T, C, A, G. Each row starts with a small label (first + "·" + third base, e.g.
T·T), followed by 4 cells for second base T, C, A, G; the codon is first + second + third (row T·T: TTT, TCT, TAT, TGT). -
Each cell shows the codon (mono, small), the amino acid one-letter code and the count; background
heatColor(count / max), white text when the color is dark; stop codons (TAA, TAG, TGA) with a light grey background and "Stop";title= "TTT → F (Phe) · 12 codons · 34.5 per 1,000". -
Below the grid, a small legend: a 160×10 px gradient bar from
#FFF4D6to#552301labelled "0" and the max count. -
The card must not widen the page: add
min-width:0to.ss-chart-card/.ss-codon-grid > *, and at ≤ 480 px use a smaller cell font (e.g. .62rem) so all 4 columns fit in 390 px. Runpython3 build.py --check.
-
-
Phone: the motif input covers the Find button. At 390 px on the Tools tab, the
#ss-motiftext input overlaps the "Find" button, so a tap on "Find" lands on the input (the browser reports "input#ss-motif intercepts pointer events" when clicking#ss-motif-btn). At ≤ 600 px make.ss-tool-rowwrap (flex-wrap: wrap), give the motif inputflex: 1 1 100%and put the button on its own line, with no overlap. Runpython3 build.py --check. -
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS repeatedly.
-
All three items are in
renderMap()(src/app.js, around line 325) plus a few lines insrc/body.html/src/app.css.state.orfsis already computed bycomputeOrfs()(each ORF hasframe"+1"…"-3",strand,start≤end,aaLen). -
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. Do not open
index.html. -
The map does not show ORFs (a required feature). SPEC.md asks for a "circular/linear sequence map with annotated ORFs (six frames, minimum length slider)". Today
renderMap()draws only the GenBank CDS and restriction sites. Add six frame tracks drawn fromstate.orfs:-
Circular records: six thin concentric rings inside the backbone circle, radii
R-34, R-46, R-58for +1, +2, +3 andR-74, R-86, R-98for −1, −2, −3; each ORF is an arc (stroke-width 8, round caps) from its start to end angle. Small labels "+1" … "−3" at the 12 o'clock end of each ring. -
Linear records: make the viewBox
0 0 900 300; put six lanes below the backbone (e.g. y = 150, 164, 178 for +1…+3 and 198, 212, 226 for −1…−3), each ORF arectheight 9, rx 3, with the frame label at the left margin. -
Forward ORFs Pride Gold
#F1B300, reverse ORFs Lupine#5E48FF; the selected ORF (state.selectedOrf) drawn with a black 2-px outline. Each ORF shape has a<title>like "Frame +1 · 891–2,633 · 581 aa · J" and a click handler that setsstate.selectedOrf, callsrenderOrfSelect(),renderCodonPanel()and re-renders the map, and fills#ss-tr-start/#ss-tr-endwith the ORF's coordinates. Add "ORF (forward)" / "ORF (reverse)" to the legend.
-
-
Minimum-length slider on the map panel. The only ORF length slider is
#ss-orf-minon the ORFs tab. Insrc/body.htmladd to the map panel's.ss-panel__controlsa second control: label "Minimum ORF length <span id="ss-map-orf-min-val">100</span> aa" and<input type="range" id="ss-map-orf-min" min="30" max="500" step="10" value="100">. Both sliders must stay in sync: oninputeither one setsstate.orfMin, updates both value labels and both slider positions, callscomputeOrfs()and re-renders the map (and the ORFs tab if it is visible). Dragging the slider on the map tab must visibly add/remove ORF arcs. -
Map labels collide and colors are ambiguous.
-
Circular map: pEGFP-N1 has 8 restriction sites between 613 and 660 bp, and all 8 labels ("XhoI 613", "HindIII 622", "EcoRI 629", "PstI 634", "KpnI 645", "SmaI 656", "BamHI 660") are drawn on top of each other in one unreadable blob at 1 o'clock. Sort the site labels by angle and spread them: when a label would be closer than 12 px (vertically) to the previous one on the same side, move it down/up, and draw a thin leader line from the tick to the moved label (text-anchor "start" on the right half of the circle, "end" on the left half). Same rule for CDS labels: "neomycin phosphotransferase" is centered on its arc and crosses it — use text-anchor "start"/"end" by side and place the label outside the arc.
-
Linear map: the ruler labels (8k, 16k, 24k, 32k) are hidden behind CDS bars and restriction ticks, and restriction ticks have no labels. Put the ruler under the lowest ORF lane, and label restriction sites above the CDS bars with the enzyme name (skip a label if it would overlap the previous one; the
<title>still gives name + position). -
Colors: EcoRI ticks use
#008080, identical to "CDS (forward)", and NotI uses#D7003F, identical to "CDS (reverse)", so the legend is ambiguous. Draw all restriction-site ticks and labels in Steel#323232(the label names the enzyme), keep the enzyme chips' colored dots as they are only if they no longer appear on the map — otherwise make the chip dots Steel too. CDS stay Clearwater (forward) / Raspberry (reverse), ORFs are gold / Lupine as in item 1.
-
-
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS repeatedly.
-
Make one change, run
python3 build.py --check, then 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. -
The map-panel slider does nothing. Dragging
#ss-map-orf-minon the Sequence map tab snaps it straight back and nothing changes (set it to 200 → label still says "300 aa", same ORF arcs). Cause:onOrfMinInput()ininit()always reads$("ss-orf-min").value(the ORFs-tab slider), so the map slider's new value is overwritten. Fix: make the handler use the value of the slider that fired the event (this.value/e.target.value), then sync the other slider, both labels,computeOrfs(),renderMap(), andrenderORFs()if that tab is visible. -
Circular map: restriction labels still overprint, and labels are clipped. Load U55762 (pEGFP-N1):
-
The 7 multiple-cloning-site labels (XhoI 613, HindIII 622, EcoRI 629, PstI 634, KpnI 645, SmaI 656, BamHI 660) are still drawn on top of each other in one unreadable black blob at about 1 o'clock.
-
The CDS label on the left is clipped by the SVG edge — it reads "osphotransferase 3k" instead of "neomycin phosphotransferase".
-
"NotI 1400" on the right is cut off at "NotI 140". Expected: enlarge the circular drawing so labels have room (e.g.
viewBox "-120 -40 760 600"keeping the circle at 260,260), and lay out the restriction labels as a column: split sites into right half / left half by angle, sort each side by the y of its tick, then walk the list and setlabelY = max(tickY, previousLabelY + 13); place the label at a fixed x just outside the ring on that side (e.g.cx ± (R + 70)), text-anchor "start" on the right and "end" on the left, and draw a thin Steel polyline from the tick to the label. Sites closer than 1 px apart after that still get their own line. Every label text must be fully inside the viewBox. Use the product name for CDS without a gene ("neomycin phosphotransferase"), placed with the same side-anchoring so it never crosses its arc. Also: the selected ORF is currently drawn withstroke="none"plus a thin black line, so it looks like a black hairline. Keep the ORF's gold/Lupine arc and draw the 2-px black outline around it (e.g. draw a black arc with stroke-width 12 first, then the colored arc with stroke-width 8 on top).
-
-
"Export results as CSV" is still incomplete. SPEC.md requires exporting results as CSV. Only "Export ORFs as CSV" and the motif-hit CSV exist. Add three buttons that use the existing
download()helper, with file names that start with the accession:-
Map panel, under the restriction table: "Download restriction sites (CSV)" — columns
enzyme,site,position(one row per site, for the enzymes currently switched on) →U55762_restriction_sites.csv. -
GC panel, under the charts: "Download GC profile (CSV)" — columns
window_start,window_size,gc_percent,gc_skewfrom the same points the chart uses →U55762_gc_profile.csv. -
Codon usage panel, under the grid: "Download codon usage (CSV)" — 64 rows, columns
codon,amino_acid,count,per_1000for the selected ORF →U55762_codon_usage_orf.csv.
-
-
Two help notes are out of date.
-
Codon usage panel note (src/body.html) still says "rows = amino acid, columns = the codons that encode it" — the grid is now the standard genetic-code layout. Change it to: "Standard genetic-code layout: rows are first·third base, columns are the second base. Darker = used more often in the selected ORF."
-
ORFs panel note says "Predicted ORFs (gold) are shown alongside annotated CDS (teal) in all six frames" but the frame tracks show forward ORFs in gold and reverse ORFs in Lupine and no CDS. Change it to: "Forward-strand ORFs are gold, reverse-strand ORFs are violet. The Match column names the annotated GenBank CDS an ORF corresponds to. Click a row or a bar to analyse that ORF."
-
-
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS repeatedly.
-
Make one change, run
python3 build.py --check, then 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. -
Circular map: CDS labels are clipped or collide. On U55762 the right-hand CDS label is cut off by the SVG edge ("enhanced green fluorescent pr…"), and the left-hand label "neomycin phosphotransferase" is drawn on top of the "3k" tick label. In
renderMap(): label a CDS with itsgenewhen it has one ("egfp"); otherwise shortenproductto at most 18 characters with "…" ("neomycin phosphot…"), keeping the full product and coordinates in the<title>. Place CDS labels with the same side-column + leader-line method you already use for restriction sites (they may share the column logic), and draw the bp tick labels inside the backbone (radiusR - 16, font-size 8) so they never collide with outside labels. Every label must be fully inside the viewBox. -
GC charts have no hover readout. Moving the mouse over the GC content or GC skew chart changes nothing. In
drawLineChart()add a transparentrectcovering the plot area; onmousemovefind the nearest point, draw a vertical Steel crosshair line and a dot on the line, and show a small tooltip box inside the SVG (or an absolutely positioned div) with "Position 21,501 bp · GC 38.4 % · skew −0.12"; hide it onmouseleave. Also draw the whole-sequence average GC as a dashed Silver line with the label "mean 49.9 %" on the GC content chart. Keep the existing 150–250 ms feel (e.g.transition: opacity .15son the tooltip). -
Translation tool only offers forward frames. "Translate a selection" has frames 1, 2, 3 only and its note says "on the forward strand". Add options −1, −2, −3 to
#ss-tr-frame: for a negative frame translate the reverse complement of the selected range (frame −1 starts at the last base). Show the result in blocks of 10 amino acids with the amino-acid position number at the start of each line (e.g.1 MALWMRLLPL LALLALWGPD …, 60 per line), and update the note to "…on either strand (frames −1…−3 use the reverse complement)". -
The first screen shows no analysis. At 1440×900 the intro card and the paste card fill the viewport; the summary cards start at y≈700 and the map tabs at y≈850. At 390 px the analysis is ~1,500 px down. Make the top compact: turn the intro into a slim banner (the heading plus one sentence, with the three "How to use" steps inside a
<details><summary>How to use</summary>…</details>), and move the drop zone into the Sequence card as a compact row next to "Parse & load" (a "Choose file…" button, and the whole Sequence card accepts drag-and-drop with the existingis-dragoverhighlight). Goal: at 1440×900 the summary cards and the tab bar are visible without scrolling, and at 390 px the tab bar is within the first ~900 px. -
Codon panel layout: big empty area and a clipped label. On the Codon usage tab the "Amino-acid composition" card sits beside the tall codon grid and leaves ~650 px of empty white space under its chart, and the tallest bar's value label ("104") is cut off by the top edge of the SVG (its text starts at y = −3). Stack the two cards vertically (codon grid full width, then amino-acid composition full width with a taller
viewBox, e.g. 900×260), and add ~16 px of top padding inside the chart so value labels are never clipped. -
The reviewer already confirmed each diagnosis in a real browser. Do not re-verify, and do not grep the CSS repeatedly.
-
Make one change, run
python3 build.py --check, then 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. -
Phone: the linear map is unreadable. At 390 px wide, lambda's linear map SVG (viewBox 900×300) is scaled to 320×107 px, so its text renders at 2.8 px — the ORF lanes, enzyme labels and ruler are a thumbnail. For linear records at viewport widths ≤ 700 px, keep the SVG at a readable size inside a horizontally scrollable wrapper: give
#ss-mapoverflow-x: auto; -webkit-overflow-scrolling: touchand the linear SVGmin-width: 820px(circular maps stay as they are), plus a small note above it on phones only: "Swipe sideways to explore the map →". The page itself must still not scroll horizontally. -
Phone: GC charts are tiny and labels overlap. At 390 px the GC content / skew charts (viewBox 700×200) render axis labels at about 4 px, the x-axis title "position (bp)" is cut off at the bottom, and the "mean 49.9 %" label is drawn on top of the data line (also at 1440 px it overprints the line near the right end). In
drawLineChart(): when the container is narrower than 600 px, use a narrower, taller viewBox (e.g. 380×240) with padding-bottom ≥ 36 so the axis title fits and fonts stay ≥ 10 px on screen; draw the mean label at the left end of the dashed line, above it, with a white background rect (orpaint-order: stroke; stroke: #fff; stroke-width: 3) so it stays readable. Re-render the charts on window resize (debounced). -
Validation rejects real inputs. Every one of these pastes fails with the same "Record '…' contains invalid characters (expected A/C/G/T/N)." and nothing loads:
-
lowercase RNA
>rna1/acguacguaugcccuaa→ expected: accepted,Uconverted toT, status notes "converted U→T". -
IUPAC ambiguity
>amb1/ACGTRYACGTNNNNACGT→ expected: accepted with a warning "2 ambiguity codes (R, Y) treated as N". -
protein
>prot1/MALWMRLLPLLALLALWGPDPAAA→ expected: refused with "Record 'prot1' looks like a protein sequence — Sequence Studio analyses DNA/RNA". -
two records, one good one bad (
>good/ATGAAATAA,>bad/ACGT123XYZ) → expected: digits and spaces ignored,goodloads,badis skipped, status "Loaded 1 of 2 records — skipped 'bad': invalid characters X, Y, Z at positions 5–7". Show the status as a short per-record summary (name · length · GC % · warnings) in#ss-status.
-
-
Motif search only covers one strand. The Tools note says "Search the forward strand", so e.g. a non-palindromic motif such as
GGTACCTTmisses reverse-strand hits. Search both strands: also run the pattern against the reverse complement and report those hits with forward-strand coordinates and a strand badge ("+" / "−") on each hit row; add astrandcolumn to the hits CSV; update the note to "Searches both strands…". Keep the gold<mark>highlight. -
Phone: the analysis still starts far down. At 390 px the tab bar is at y ≈ 1,068 px because the intro card repeats a long sentence and the Sequence card stacks label, textarea and buttons. At ≤ 600 px: hide the intro paragraph (keep the heading and the "How to use" details), make the intro heading 1.15 rem, and give the paste textarea 2 rows. Goal: tab bar within the first ~850 px on a 390×844 phone.
-
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 insrc/body.html). Make one change, runpython3 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.gbfiles. -
Motif search highlights the wrong bases on reverse-strand hits (
toolMotif()). For a reverse-strand hit,h.posis already the leftmost forward-strand coordinate (n - (m.index + motif.length - 1)), but the display and the CSV useseq.slice(pos - motif.length, pos), which is shifted left bymotif.length - 1. Example: insulin (NM_000207), motifCTGCAG, the "−" hit at 240 highlightsAGGACCinstead ofCTGCAG; pEGFP-N1 (U55762), motifGAATTC, the "−" hit at 629 highlightsGCTTCG. Fix: for both strands the matched forward region isseq.slice(pos - 1, pos - 1 + motif.length)with left contextseq.slice(max(0,pos-6), pos-1)and right contextseq.slice(pos-1+motif.length, pos+motif.length+4). In the CSV, writematched_sequenceas the actual motif-strand sequence: the forward slice for "+", andreverseComplement(forward slice)for "−". -
Palindromic motifs are counted twice, and overlapping hits are missed (
toolMotif()).GAATTCin U55762 reports "2 hits" for its single EcoRI site (one "+" and one "−" at 629);CTGCAGin NM_000207 reports 6 hits for 3 sites (240, 288, 315). Fix: after collecting both strands, if a "−" hit has the sameposas 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 thewhile (regex.exec(...))loop resumes after the whole match, so overlapping hits are skipped (e.g.GGNCCin U55762 gives 24 rows, but there are 26 strand hits = 13 + 13). Setregex.lastIndex = m.index + 1after every match on both strands so overlaps are found. -
The motif placeholder suggests syntax the search rejects.
#ss-motifinsrc/body.htmlhasplaceholder="e.g. GAATTC or A[CG]T", but typingA[CG]Tgives 'Invalid character "[" at position 2'. Change the placeholder toe.g. GAATTC or GGNCC(IUPAC letters only). -
Partial ORFs show fractional lengths (
findORFs()). When no stop codon is found,end = s.seq.length, soaaLen = (end - start) / 3can be non-integer: NM_000207 at minimum 30 aa lists "+2 · 341 · 465 · 41.667 aa · partial". Fix: for an ORF without a stop, trimendto the last complete codon (end = start + 3 * Math.floor((s.seq.length - start) / 3)) before computingaaLenand the coordinates, so the length is an integer (41) and the End column is the last base of the last full codon. -
One bad record makes the whole paste fail with a misleading message (
parseFasta()). Pasting three valid records plus>bad digits/ACGT123XYZloads nothing and says "Record 'bad' looks like a protein sequence". The same happens for>bad2/ACGTXXJJACGT. Two fixes: (a) When buildingcur.seq, strip digits as well as whitespace (line.replace(/[\s\d]+/g, "")) — numbered sequence lines are common. (b) Do notthrowwhenlooksLikeProtein(seq)is true; insteadskipped.push({ id: r.id, reason: "looks like a protein sequence" })andcontinue, 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."). -
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()insrc/app.js; item 2 is one CSS rule insrc/app.css; item 4 is inrenderMap()plus a small element insrc/body.html. Make one change, runpython3 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.gbfiles. -
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 atfont-size8–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 inrenderMap()(circular branch only): raise the SVG max width to1000px; 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 toprevY + 16; and show the full product name "neomycin phosphotransferase" (do not truncate at 18 chars). At viewport widths ≤ 700 px give the circular SVGmin-width: 640pxinside the existing horizontally scrollable#ss-mapso text stays ≥ 10 px on screen. -
"Swipe sideways to explore the map →" shows above the circular map on phones.
renderMap()correctly setsswipeNote.hidden = isCircular, but the phone media query insrc/app.csshas.ss-map__swipe-note { display: block; }, which overrides thehiddenattribute. 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.) -
GC charts: the x-axis title is clipped and the mean label sits on the data. In
drawLineChart()the "position (bp)" text is placed aty: 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 atH - 8. Increase the bottom padding (padB32 → 46 on wide, 40 → 52 on narrow), draw the tick labels atpadT + plotH + 16and the axis title atpadT + 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. -
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; }plustransition: opacity .15s); (b) a single custom tooltip<div class="ss-map-tip">positioned near the pointer onmousemoveover 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 onmouseleave; (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 callsswitchTab("codon"). -
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.gbfiles. -
Every unmatched ORF is labelled "J" (
updateMapSelected()insrc/app.js). The linevar 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: whencdsis null, leave the name out entirely: "Selected ORF: Frame −2 · 16,438–18,129 · 564 aa — see Codon usage and Tools". -
The left-hand gene label on the circular map is cut off (
renderMap(), circular branch). WithviewBox "-150 50 820 430", the label "neomycin phosphotransferase" (text-anchorendat 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. -
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) butscrollLeftstays 0, so the ring is cut off on the right, and the swipe note is hidden becauseswipeNote.hidden = isCircular. Fix: afterwrap.appendChild(svg), setwrap.scrollLeft = Math.max(0, (wrap.scrollWidth - wrap.clientWidth) / 2)for circular maps (keep 0 for linear maps), and setswipeNote.hidden = !(wrap.scrollWidth > wrap.clientWidth + 4)for both map types (so it shows only when the map really scrolls). -
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 whenmatchCds()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. -
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)."
1 build 16.3 min · 6 files changed, 4008 insertions(+), 3 deletions(-) Show prompt
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.
2 feedback 5.2 min · 1 file changed, 113 insertions(+) Show prompt
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.
Reviewer feedback — round 1 (tested in Chromium at 1440×900 and 390×844)
Most important first. Fix every item and keep what already works (brand header, intro card, tabs, restriction table values, GC content chart, translate/reverse-complement tools).
>test_one Synthetic test record one
ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCGGGCTGAGAATTCGGATCCAAGCTTTTTT
>test_two Second synthetic record
GGGCCCTTTAAACCCGGGATGCCCAAAGGGTTTCTCGAGGCGGCCGCTTGACCCATGGATCCTAGGTACCCTGCAGTAA
3 continue 27.8 min · — Show prompt
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.
4 continue 25.9 min · 1 file changed, 62 insertions(+), 106 deletions(-) Show prompt
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.
Reviewer feedback — batch 1 of 2 (tested in Chromium at 1440×900 and 390×844)
Working notes (please read first)
Items
>test_one Synthetic test record one
ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCGGGCTGAGAATTCGGATCCAAGCTTTTTT
>test_two Second synthetic record
GGGCCCTTTAAACCCGGGATGCCCAAAGGGTTTCTCGAGGCGGCCGCTTGACCCATGGATCCTAGGTACCCTGCAGTAA
5 continue 3.2 min · 4 files changed, 89 insertions(+), 89 deletions(-) Show prompt
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.
Reviewer feedback — batch 1 (five focused bugs)
How to work on this (please follow)
Items
6 feedback 18.4 min · 1 file changed, 56 insertions(+), 35 deletions(-) Show prompt
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.
Reviewer feedback — batch 2 (six focused items)
Batch 1 is fixed and verified: pasted/picked/dropped FASTA now loads, ORFs are no longer duplicated, reverse ORFs have correct coordinates and codon usage, GC skew uses (G−C)/(G+C). Keep all of that.
How to work on this (please follow)
Items
7 continue 4.2 min · 4 files changed, 127 insertions(+), 92 deletions(-) Show prompt
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.
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)
Items
8 feedback 1.4 min · 6 files changed, 179 insertions(+), 60 deletions(-) Show prompt
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.
Reviewer feedback — batch 2b (three focused items)
Batches 1 and 2a are fixed and verified in the browser: user FASTA loads, ORFs correct on both strands, GC skew formula, codon tab filled on first visit, Clear resets chips, Data & sources always visible with correct links, the three examples auto-load with U55762 active. Keep all of that.
How to work on this (please follow)
Items
9 feedback 3.1 min · 6 files changed, 314 insertions(+), 159 deletions(-) Show prompt
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.
Reviewer feedback — batch 3 (three focused items)
Verified fixed in the browser: topology is now linear / linear / circular, the circular map is drawn for U55762, motif hits are highlighted with <mark> and can be downloaded as CSV. Keep all of that.
How to work on this (please follow)
Items
10 feedback 2.7 min · 4 files changed, 374 insertions(+), 99 deletions(-) Show prompt
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.
Reviewer feedback — batch 4: the sequence map (three focused items)
Verified fixed in the browser: definition text is short again, no page-level horizontal scroll on any tab at 1440 px or 390 px, the new genetic-code codon grid looks great on desktop and phone, and the phone motif "Find" button is tappable. Keep all of that.
How to work on this (please follow)
Items
11 feedback 2.5 min · 4 files changed, 266 insertions(+), 127 deletions(-) Show prompt
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.
Reviewer feedback — batch 5 (four focused items)
Verified in the browser: the linear map now looks excellent (six labelled ORF lanes, enzyme labels above the CDS, ruler below, selected ORF outlined), and circular records show ORF arcs in six rings. No console errors, no page-level horizontal scroll. Keep all of that.
How to work on this (please follow)
Items
12 feedback 3.4 min · 5 files changed, 376 insertions(+), 184 deletions(-) Show prompt
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.
Reviewer feedback — batch 6 (five focused polish items)
Verified in the browser: the map slider now works and stays in sync, circular restriction labels are cleanly spread with leader lines, mouse clicks on map ORF arcs select the ORF and fill the translate fields, all four CSV downloads work with the right headers, help notes updated, no console errors, no horizontal scroll at 1440 px or 390 px. Keep all of that.
How to work on this (please follow)
Items
13 feedback 4.5 min · 5 files changed, 469 insertions(+), 125 deletions(-) Show prompt
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.
Reviewer feedback — batch 7 (final five items)
Verified in the browser: compact first screen on desktop, circular map labels clean (gene/product in leader-line columns, tick labels inside the ring), GC hover tooltip and mean line, translation in frames ±1–3 with numbered blocks of 10, stacked codon grid + amino-acid chart, all CSV downloads, no console errors, no page-level horizontal scroll. Keep all of that.
How to work on this (please follow)
Items
14 critic 1.1 min · 4 files changed, 120 insertions(+), 60 deletions(-) Show prompt
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.
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)
Items
15 critic 4.5 min · 5 files changed, 211 insertions(+), 78 deletions(-) Show prompt
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.
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)
Items
16 critic 10.9 min · 3 files changed, 50 insertions(+), 53 deletions(-) Show prompt
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.
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)
Items
What the agent was asked
The full specification the harness received, rendered from the original Markdown.
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).
