Files
dela 866173a831 Add source linter, negative test fixtures, and fallback guidance
- scripts/lint.py: reject raw rectangles, absolute coordinates, hue-budget
  and callout/group/formula-order violations at the source level
- tests/invalid/ + tests/lint-invalid/: negative fixtures proving the
  package and linter reject bad input; test.sh now runs both directions
- references/fallback.md: degraded path when no LaTeX is available
- tests/group-callout.tex: exercise \stgroup and \stcallout
- agents/openai.yaml: agent config
- Docs and .sty updated to match
2026-08-05 16:41:22 +08:00

4.4 KiB
Raw Permalink Blame History

Anti-patterns

Every figure below compiles cleanly. build.sh is happy with all of them. They are still wrong, because the compiler checks TeX syntax and not whether the picture is true.

Render examples/antipatterns.tex and look at examples/build/antipatterns.png once before your first figure.

1. The transpose that only changed its label

A face captioned Kᵀ that is still T × d_h. The reader looks for the contracted axis, finds two faces of the same height, and concludes the contraction runs along the wrong dimension. Fix: swap the arguments — \ststack{KT}{...}{dh}{T}{3}. See geometry.md §3.

2. Shards that do not tile their parent

Two shards drawn with a gap, or stretched to fill a parent whose other shards were elided. Both assert a width that the tensor does not have. Fix: draw the shards in one band and give every shard after the first gap=0pt, which states that they are adjacent instead of arranging for it; draw an ellipsis for anything omitted. See geometry.md §5–6.

3. An index drawn as a heatmap

Expert ids or token positions rendered with a lightness ramp. The ramp is a magnitude channel, so it says expert 3 > expert 0, which is meaningless. Fix: \stindexface. See semantics.md.

Same family: a Boolean mask drawn with graded cells (it has one level, not three), and a score matrix drawn as flat blocks (it has magnitude, and hiding it wastes the figure).

4. One pale level everywhere

A whole tensor in role!10. At full size it looks tasteful; at thumbnail size — which is how it will be seen on a slide — it is a blank rectangle. Fix: three separated levels, role!30 / role!55 / role!80. Contrast comes from lightness, not saturation. See style.md.

  • Periodic texture. A polynomial hash reduced mod 3 repeats every 3 rows, and the eye reads the resulting stripe as real structure. pattern=dense avoids it; if you write your own filler, check that rows 1, 2, 4, 5 of a tall face are not identical.
  • A label wider than its connector. The white underlay then covers the target tensor. \stlink makes this unrepresentable: the label reserves its own width in the band and the arrow is drawn to whatever lands beside it. See layout.md.
  • A new hue for a regrouped view of the same data. X and the per-expert buffers gathered out of X are the same object in a different order; a second hue claims they are different tensors.
  • Captions hanging at different depths because the faces in a row have different heights. Use \strow/\strowend, which arms \stlane for you.
  • Hand-tuned offsets. Each one is a magic number valid only for the content that was there when you tuned it; the figure that breaks is the next one, when a label grows two characters and lands on a face. Use the cursor. See layout.md.
  • A formula line placed first. It can only be centered on a figure whose width is not known yet, so it ends up visibly off-center. Call \sttopformula after the bands.
  • A floating commentary card between two operands. If it is not a real operation, it belongs in the stage subtitle, the bottom box, or a \stcallout beside the whole band. A card anchored to a single face reads as a step in the computation, and two cards on one band turn the figure into a dashboard; both are lint errors.
  • A callout that should have been the meaning box. If the card is taller than the band it hangs off, it is not an aside — it is the Mechanism row, and leaving it as a card only opens white space, since the callout pushes the vertical cursor below itself.
  • A group border used as decoration. \stgroup names its members as one composite object; drawn around whatever happened to be adjacent, it invents a grouping the computation does not have. If you cannot caption the outline, do not draw it.
  • A group whose hue invents a new object. The outline around the three q sheets is still q. A fresh hue there claims a fourth tensor exists; use the members' role, or neutral when the members really are of mixed roles. See semantics.md.
  • A meaning box that repeats the shapes. The shapes are already under every block. The box is for what the axes mean and what the operation does.
  • Solving crowding by shrinking type. The type hierarchy is a hard floor; move the stage to another row instead.