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:
@@ -54,7 +54,18 @@ See `style.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 or the bottom box.
|
||||
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
|
||||
|
||||
+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
|
||||
|
||||
+10
-7
@@ -1,7 +1,8 @@
|
||||
# Pre-delivery checklist
|
||||
|
||||
A clean `build.sh` proves only that TeX was happy. Nothing below is checked by the compiler.
|
||||
Work through it against the rendered PNG. Any mandatory violation means redraw, not patch.
|
||||
`build.sh` already checks the source rules, package invariants, missing glyphs and text-box
|
||||
overflow. The math, semantics and rendered relationships below still require inspection.
|
||||
Any mandatory violation means redraw, not patch.
|
||||
|
||||
## 1. Math (before looking at the picture)
|
||||
|
||||
@@ -30,18 +31,20 @@ Open the PNG at 100 %. Most of the first three items are automatic under the flo
|
||||
they still need looking at, because the cursor only guarantees that objects do not *push*
|
||||
into each other, not that the figure reads correctly.
|
||||
|
||||
- [ ] No absolute coordinate that a `\strow`/`\stcol` could have expressed. Every remaining
|
||||
hand-placed node is wrapped in `\sttrack`.
|
||||
- [ ] `\sttopformula` called after the bands, so the formula is centered on the real figure.
|
||||
- [ ] Every lint exemption in the source is genuinely required and explained; deliverables
|
||||
normally have none.
|
||||
- [ ] No forbidden intersection, tangency, clipping or occlusion — including stack offset
|
||||
sheets, brackets, arrow labels and the meaning box.
|
||||
- [ ] Every connector's white label underlay covers only its own connector.
|
||||
- [ ] No connector crosses a box that is not its endpoint.
|
||||
- [ ] Symbols and shapes sit on two flat lanes per row; stage headings share a left rail.
|
||||
- [ ] Every `\stgroup` outline names a composite the computation actually has, is captioned,
|
||||
and carries its members' role hue (`neutral` only for genuinely mixed members).
|
||||
- [ ] Every `\stcallout` is an aside about its whole band, not a step: one per band, no
|
||||
taller than the band, and nothing in it that belongs in `\stmeaningbox`.
|
||||
- [ ] Top zone compact (≤2 formula lines, no shape underbraces).
|
||||
- [ ] Bottom box: one column, ≤3 rows, no overflow, no shrunken type.
|
||||
- [ ] Signature outside the box, one line, names what the figure actually shows, not clipped
|
||||
and not visually dominant.
|
||||
- [ ] If requested, signature is outside the box, one line, accurate, unclipped and subdued.
|
||||
- [ ] Structural support exact: known zeros unfilled, masks and diagonals exactly right.
|
||||
|
||||
## 5. Thumbnail audit
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Fallback without LaTeX
|
||||
|
||||
Use this path only when `scripts/preflight.sh` exits 2. A fallback is not equivalent to the
|
||||
TikZ package: say explicitly that cursor placement, ledger locking, face-data validation,
|
||||
caption lanes and package warnings are unavailable.
|
||||
|
||||
## Preserve manually
|
||||
|
||||
1. Recompute the shape/semantics ledger before drawing.
|
||||
2. Use one scale function from symbolic axis names to physical lengths; never size two
|
||||
occurrences of the same axis independently.
|
||||
3. Keep scores, indices and masks in distinct grammars; preserve exact zeros and support.
|
||||
4. Derive every placement from previous bounding boxes and one gutter constant.
|
||||
5. Export SVG plus PNG and inspect both full-size and at 360 px using `checklist.md`.
|
||||
|
||||
Prefer SVG for editability. Use matplotlib only when it can emit SVG and the tensor cells
|
||||
remain individually inspectable. Do not imitate package compliance in the delivery: name
|
||||
the fallback renderer and list any inferred shape, convention or reduced guarantee.
|
||||
@@ -17,8 +17,12 @@ to exactly one physical edge length across the whole figure**. Equal shapes ther
|
||||
an equivalence class automatically: `Q` and `V` at `T×d_h` come out identical without you
|
||||
lining anything up by hand.
|
||||
|
||||
Declaring the same axis twice with the same value is allowed. Redeclaring it with a
|
||||
different value emits a package warning, preserves the first value, and fails `build.sh`.
|
||||
|
||||
Raw integers are accepted (`\stface{A}{(0,0)}{4}{4}`) but they opt out of the guarantee.
|
||||
Use them only for a face whose axis appears nowhere else.
|
||||
Use them only for a face whose axis appears nowhere else. The source linter rejects a
|
||||
repeated raw dimension greater than one; give repeated dimensions a symbolic name.
|
||||
|
||||
## The rules
|
||||
|
||||
|
||||
+18
-3
@@ -34,6 +34,7 @@ Overlap is allowed only inside one declared composite:
|
||||
- shards tiling a parent,
|
||||
- outline sheets in one `\ststack`,
|
||||
- a bracket around its own tensor,
|
||||
- a `\stgroup` outline around its own members,
|
||||
- a connector endpoint touching its source/target border.
|
||||
|
||||
Every other intersection or occlusion is forbidden.
|
||||
@@ -68,9 +69,23 @@ Stage headings share one left rail. `\ststage` puts them there: the rail is a si
|
||||
`x` (`\stleftrail` to move it), and the `y` is derived from the lowest ink drawn so far, so
|
||||
a heading can neither drift right nor collide with the row above it.
|
||||
|
||||
Explanatory prose belongs in the stage subtitle, the bottom box, or above its own
|
||||
connector. Never drop a floating commentary card between two operands unless it is a real
|
||||
operation node (`st comm`).
|
||||
Explanatory prose belongs in the stage subtitle, the bottom box, above its own connector,
|
||||
or on a `\stcallout` card hanging off the right edge of a band. Never drop a floating
|
||||
commentary card between two operands unless it is a real operation node (`st comm`).
|
||||
|
||||
`\stcallout` is that rule made structural: it refuses to open inside a band, the linter
|
||||
rejects it unless it is anchored to a `\strow` name, and a second card on one band is an
|
||||
error — side-by-side cards are a dashboard, not a figure. A card that ends up much taller
|
||||
than its band is telling you the same thing the overflow warning does: that text is not an
|
||||
aside, it is the **Mechanism** row of `\stmeaningbox`.
|
||||
|
||||
## Composites
|
||||
|
||||
`\stcol` and `\stgroup` are the two sub-flows, and both exist so that a *relationship* can
|
||||
be stated rather than arranged for. A group wraps its members from the inside, which is
|
||||
what makes an arrow attach to the outline rather than end inside it — a connector whose
|
||||
endpoint is a member but which crosses the group border is the ordinary version of "a
|
||||
connector may not cross a box that is not its endpoint".
|
||||
|
||||
## Layers
|
||||
|
||||
|
||||
@@ -65,3 +65,9 @@ One tensor role keeps one hue for the whole figure — that is what `\stsetrole`
|
||||
gathered, resharded or regrouped view of the same data keeps the *same* role color; a new
|
||||
hue means a new object. Derived tensors may reuse their parent's family rather than
|
||||
spending a hue (`V → O → Y` in the MHA example are all violet).
|
||||
|
||||
A `\stgroup` outline follows the same rule, because a group *is* a regrouped view: give it
|
||||
the role of the objects it wraps — the three-sheet `q` stack and the outline that names it
|
||||
as one composite are the same object, and a new hue there would claim a new tensor exists.
|
||||
Only when the members genuinely differ in role does the group take `role=neutral`; that is
|
||||
also the honest signal that the box is naming an arrangement rather than an object.
|
||||
|
||||
+18
-6
@@ -21,8 +21,9 @@ stages that preserve the primary path. No unrelated branches, no dashboard panel
|
||||
| operator | `\Large` | `st op` |
|
||||
| symbol | `\small` | `\stcaption` arg 2 |
|
||||
| shape | `\scriptsize`, muted | `\stcaption` arg 3 |
|
||||
| side card | `\scriptsize\bfseries` title, `\scriptsize` body | `\stcallout`, same tier as `st note` |
|
||||
| bottom prose | `\small` | `\stmeaningbox` |
|
||||
| signature | `\scriptsize`, low contrast | `\stsignature` |
|
||||
| optional signature | `\scriptsize`, low contrast | `\stsignature` |
|
||||
|
||||
Never shrink below this to make something fit — see `layout.md`.
|
||||
|
||||
@@ -32,6 +33,10 @@ Separate tiles with a small white gutter and 0.5–1 pt corner rounding (`\st@ti
|
||||
this). Thin neutral brackets, `black!55`–`black!70` outer borders. No saturated
|
||||
tensor-colored outlines, no continuous spreadsheet grid.
|
||||
|
||||
The one exception is `\stgroup`, whose outline is drawn at `role!65`: there the outline
|
||||
*is* the object being named, so the hue is doing semantic work rather than decorating a
|
||||
face that already has its own fill.
|
||||
|
||||
**Encode support before magnitude.** Every known zero stays white/unfilled; every shown
|
||||
nonzero gets color. A diagonal matrix must read instantly as colored diagonal cells on a
|
||||
white field. `pattern=diag/band/lower/upper/causal/data` fill exactly the structural
|
||||
@@ -69,16 +74,23 @@ Narrow bold label rail, left-aligned ragged-right `\small` content, 8–10 pt in
|
||||
Keep each row compact: prefer symbol semantics over numeric configuration. When it is too
|
||||
long, **remove content** — never add cards, columns or smaller type. Pass `{}` to omit a row.
|
||||
|
||||
## Signature
|
||||
## Side cards
|
||||
|
||||
One centered line below the box, outside it, low-contrast gray, `\scriptsize` or smaller:
|
||||
`\stsignature{<subject>}{<fit node>}` renders `<subject>@五道口纳什`. The subject must name
|
||||
what this figure actually visualizes. Keep it on one line, with a small but visible gap.
|
||||
`\stcallout` is the only sanctioned floating text card, and it is deliberately narrow in
|
||||
scope: one per band, hung off the right edge of a *finished* band, never between two
|
||||
operands. When the text outgrows the height of its band, it is not an aside — move it into
|
||||
the **Mechanism** row of `\stmeaningbox` instead of widening or shrinking the card.
|
||||
|
||||
## Optional signature
|
||||
|
||||
Add a centered line only when the user or house template requests it. Keep it below the
|
||||
box, outside it, low-contrast gray and on one line. `\stsignature{<subject>}{<fit node>}`
|
||||
renders only the subject. Do not append an author, handle or brand identity.
|
||||
|
||||
## Never
|
||||
|
||||
Charts or metric insets not present in the primary formula. Decorative pills, banners,
|
||||
shadows, repeated separators, explanatory cards.
|
||||
shadows, repeated separators, explanatory cards other than one `\stcallout` per band.
|
||||
|
||||
## Reference image
|
||||
|
||||
|
||||
Reference in New Issue
Block a user