What
tubemap.js can draw a reversal — the corners and vertical rectangles that render an inverted strand. pgb's parseBands.ts cannot read any of those shapes, and because its whole-document gate compares drawables counted against drawables matched, one unreadable shape does not degrade the picture: it refuses the entire document, and the researcher sees an error card instead of a tube map.
Found on 2026-08-28 while verifying #22 against pgb's real parser. This predates #22 and #22 did not change it in either direction — the pre- and post-#22 documents are byte-identical modulo three removed attributes, and pgb recovers bit-identical arrays from both.
Why the grammar misses them
pgb matches every band with one pattern (parseBands.ts, FILL / RECT / PATH). A reversal is off it in three independent ways:
| shape |
what the grammar wants |
what the layout emits |
| corner |
trackName="...", non-empty |
no trackName — the layout builds a corner without one |
| corner |
M x y C ... V ... C ... Z (cubics) |
M x y Q ... H ... Q ... Z (quadratics, tubemap.js:3052-3070) |
| corner and vertical rect |
the literal fill-opacity: 1; inside style |
no fill-opacity at all — the document has never painted one on these |
Any one of the three is enough. pgb then throws NonConformingDocument: "Of the N drawables in g.track, M are not bands this renderer recognises."
How live is it
Latent, not live, as far as anything committed can show. Across all five real subgraphs in tests/fixtures/seqtubemap/: zero corners, and every band carries fill-opacity: 1.
| fixture |
rect |
curve |
corner |
bands with no opacity |
| chr8 90 bp |
518 |
74 |
0 |
0 |
| chr1 600 bp |
3,725 |
4,364 |
0 |
0 |
| chr8 1.4 kb |
6,542 |
6,704 |
0 |
0 |
| chr1 8.0 kb |
16,422 |
18,598 |
0 |
0 |
| chr1 4.2 kb |
19,482 |
25,313 |
0 |
0 |
But that is a property of five regions, not of the layout. tests/node/document-conformance.test.mjs now contains a synthetic reversal — one strand sent backwards through three segments of the smoke fixture — which draws corners and which the transcribed grammar refuses. So the shape is reachable; what is unknown is whether any real region produces one.
pgb's own ADR 0004 records that a chr8p23.1 document containing an inversion was refused whole once before, for a different over-strict rule that was then withdrawn. Same failure shape, second cause.
Worth deciding, not obvious
Three possible readings, and they are not equivalent:
- The client is needlessly strict.
fill-opacity is required as a literal and then never used — it is not in ParsedMap at all. A pattern that accepted an optional opacity would cost nothing and would take one of the three causes off the table. Likewise trackName is required on every band but only read from the first band carrying each id.
- The server should not draw shapes it cannot describe. Corners are a drawing artifact of the layout, and #24's band format has to say something about them regardless.
- It genuinely never happens and the right move is to find out, by looking for a region that inverts.
(1) and (2) are cheap; (3) is the one that says whether either is urgent.
Increment C is where this stops being a regex problem. ?format=bands publishes numbers rather than a drawing, and the band data already carries corners as a first-class kind with the strand they belong to — so the client would receive them as data instead of failing to match them as a path. Worth deciding what a corner means on that route before it ships.
Acceptance criteria
What
tubemap.jscan draw a reversal — the corners and vertical rectangles that render an inverted strand.pgb'sparseBands.tscannot read any of those shapes, and because its whole-document gate compares drawables counted against drawables matched, one unreadable shape does not degrade the picture: it refuses the entire document, and the researcher sees an error card instead of a tube map.Found on 2026-08-28 while verifying #22 against
pgb's real parser. This predates #22 and #22 did not change it in either direction — the pre- and post-#22 documents are byte-identical modulo three removed attributes, andpgbrecovers bit-identical arrays from both.Why the grammar misses them
pgbmatches every band with one pattern (parseBands.ts,FILL/RECT/PATH). A reversal is off it in three independent ways:trackName="...", non-emptytrackName— the layout builds a corner without oneM x y C ... V ... C ... Z(cubics)M x y Q ... H ... Q ... Z(quadratics,tubemap.js:3052-3070)fill-opacity: 1;insidestylefill-opacityat all — the document has never painted one on theseAny one of the three is enough.
pgbthen throwsNonConformingDocument: "Of the N drawables in g.track, M are not bands this renderer recognises."How live is it
Latent, not live, as far as anything committed can show. Across all five real subgraphs in
tests/fixtures/seqtubemap/: zero corners, and every band carriesfill-opacity: 1.But that is a property of five regions, not of the layout.
tests/node/document-conformance.test.mjsnow contains a synthetic reversal — one strand sent backwards through three segments of the smoke fixture — which draws corners and which the transcribed grammar refuses. So the shape is reachable; what is unknown is whether any real region produces one.pgb's own ADR0004records that a chr8p23.1 document containing an inversion was refused whole once before, for a different over-strict rule that was then withdrawn. Same failure shape, second cause.Worth deciding, not obvious
Three possible readings, and they are not equivalent:
fill-opacityis required as a literal and then never used — it is not inParsedMapat all. A pattern that accepted an optional opacity would cost nothing and would take one of the three causes off the table. LikewisetrackNameis required on every band but only read from the first band carrying each id.(1) and (2) are cheap; (3) is the one that says whether either is urgent.
Note for #23 / #24
Increment C is where this stops being a regex problem.
?format=bandspublishes numbers rather than a drawing, and the band data already carries corners as a first-classkindwith the strand they belong to — so the client would receive them as data instead of failing to match them as a path. Worth deciding what a corner means on that route before it ships.Acceptance criteria
pgband its own fixtures cover a reversaltests/node/document-conformance.test.mjs's reversal test updated to assert whatever the decision makes true