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
This commit is contained in:
+59
-2
@@ -8,18 +8,26 @@
|
||||
Build with `./scripts/build.sh fig.tex` — it puts `assets/` on `TEXINPUTS`, so the package
|
||||
does not need to be installed into your texmf tree.
|
||||
|
||||
The build first runs `scripts/lint.py`. It rejects ledger changes, untracked absolute
|
||||
objects, repeated anonymous dimensions, raw TikZ rectangles, a formula placed before the
|
||||
last row, an unclosed `\stgroup`, a callout anchored to anything other than a band or a
|
||||
second callout on one band, and more than four active hue families per row. Intentional galleries/tests may
|
||||
put `% supertensor-lint: allow-absolute, allow-missing-formula` near the top; do not add an
|
||||
exemption to a deliverable merely to make it pass.
|
||||
|
||||
## Ledgers
|
||||
|
||||
```tex
|
||||
\stsetrole{q}{stTeal} % role -> color. Macros take a ROLE, never a color.
|
||||
\stdim{T}{6} % symbolic axis -> physical edge length in cells
|
||||
\stsetauthor{...} % default 五道口纳什
|
||||
\stsetlabels{A}{O}{M} % override the three meaning-box rail labels
|
||||
\stsetrail{3.2em} % width of the bold label rail
|
||||
```
|
||||
|
||||
Colors: `stTeal stOrange stCoral stViolet stGray stInk`. An unknown role falls back to gray
|
||||
**and emits a package warning**, which `build.sh` turns into a failed build.
|
||||
Redeclaring an axis or role with the same value is harmless; changing its value emits a
|
||||
warning and keeps the original mapping.
|
||||
|
||||
Lengths: `\stunit` (one cell, 4.6 mm) and `\sttilegap` (white gutter, 0.5 mm).
|
||||
|
||||
@@ -48,6 +56,7 @@ the next tensor, and two stages started from two different `x` share no rail.
|
||||
| `\strow{name}{height}` | open a band; `height` is an axis name or an integer |
|
||||
| `\strowend` | `fit` the band into `name`, then `\stlane` it |
|
||||
| `\stcol{name}{height}` … `\stcolend` | vertical sub-flow filling one slot of the band |
|
||||
| `\stgroup[role=]{name}` … `\stgroupend` | outline naming the objects inside it as one composite |
|
||||
| `\stglyph{name}{$\times$}` | operator glyph (not `\stop` — plain TeX owns that name) |
|
||||
| `\stcomm{name}{All-Reduce}` | collective node |
|
||||
| `\stnode[style]{name}{text}` | any node, placed and measured by the cursor |
|
||||
@@ -90,6 +99,50 @@ is a warning too.
|
||||
Absolute placement still works everywhere — pass a coordinate instead of `{}`. Mix freely,
|
||||
but wrap hand-placed nodes in `\sttrack` so the cursor knows about them.
|
||||
|
||||
## Groups
|
||||
|
||||
`\stgroup` … `\stgroupend` is the second sub-flow. It draws a thin rounded outline in the
|
||||
role hue around whatever is placed between them, naming those objects as one composite:
|
||||
|
||||
```tex
|
||||
\stgroup[role=q]{qg} % keys: role= (hue), pad= (default \stgrouppad)
|
||||
\ststack[role=q]{qh}{}{T}{dh}{3} % "these three sheets are q"
|
||||
\stgroupend
|
||||
\stcaption{qg}{$\mathbf q$}{$B\times T\times h\times d_h$}
|
||||
```
|
||||
|
||||
The members go **inside** the block, and three properties follow from that:
|
||||
|
||||
- a group can only wrap *adjacent* objects — one that reached across the band would
|
||||
swallow whatever sat between its members;
|
||||
- the group, not its last member, terminates a pending `\stlink` and sources the next
|
||||
one, so an arrow lands **on** the outline instead of ending inside it and crossing a
|
||||
border that is not its endpoint;
|
||||
- the padding is reserved on both sides, so the neighbour cannot land tangent to it.
|
||||
|
||||
`\strowend` fits the group, so `\stcaption{qg}{...}` hangs from the caption lane below the
|
||||
outline, not below the member. A group may contain a `\stcol`; it may not sit inside one,
|
||||
and it may not nest. An unclosed group is a lint error.
|
||||
|
||||
## Callouts
|
||||
|
||||
`\stcallout{name}{text width}{band}{title}{body}` hangs a side note card off the right edge
|
||||
of a **finished** band, top-aligned with it. Call it after `\strowend`:
|
||||
|
||||
```tex
|
||||
\strowend
|
||||
\stcaption{g}{$\mathbf g$}{$B\times T\times h\times d_k$}
|
||||
\stcallout{n1}{5.2cm}{rowB}{下界化换来了什么}{Kimi Linear 的 $g$ 无下界……}
|
||||
```
|
||||
|
||||
Arg 3 must be a `\strow` band name — one aside per band, and never anchored to a single
|
||||
face. Both rules are lint errors, because a card beside one operand reads as a step in the
|
||||
computation (`layout.md`). Inside an open band it is a package error.
|
||||
|
||||
A callout pushes the vertical cursor below its own bottom edge. A card taller than its band
|
||||
therefore opens visible white space rather than colliding with the next stage — which is
|
||||
the signal that its text belongs in `\stmeaningbox` instead.
|
||||
|
||||
## Faces
|
||||
|
||||
```tex
|
||||
@@ -118,6 +171,8 @@ Keys:
|
||||
|
||||
`\stindexface` entries are row-major, `rows*cols` of them; `.` leaves a cell blank.
|
||||
It deliberately has no lightness ramp — see `semantics.md`.
|
||||
The package validates the entry count, pattern name, level range, and every `pattern=data`
|
||||
row's count, width and `0`–`3` domain; any mismatch makes `build.sh` fail.
|
||||
|
||||
```tex
|
||||
\stface[role=w, pattern=data, level=3,
|
||||
@@ -167,13 +222,15 @@ Connectors route on the background layer automatically.
|
||||
```tex
|
||||
\stbbox{all} % flow: everything drawn so far
|
||||
\stmeaningbox{mb}{16.6cm}{all}{axes text}{objects text}{mechanism text}
|
||||
\stsignature{因果多头注意力(掩码 + 拼接投影)}{mb}
|
||||
\stsignature{因果多头注意力(掩码 + 拼接投影)}{mb} % optional
|
||||
```
|
||||
|
||||
Arg 2 is the total box width; arg 3 is the node it hangs below. `\stbbox` already contains
|
||||
every caption and top label; if you build the `fit` by hand, include them yourself or the
|
||||
box will overlap them. An empty `{}` row is dropped.
|
||||
|
||||
`\stsignature` renders only its subject; it has no author or handle mechanism.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- `\ststack` never re-enters `\stface`; if you extend the package, do not pass
|
||||
|
||||
Reference in New Issue
Block a user