supertensor lint.py now rejects role= used before \stsetrole, matching
superfig and superderive lint behavior. Previously this was only caught
at TeX compile time by \stcheckrole.
- Bracket ink is part of the fit: \st@facebody drops -inkw/-inke extreme
coordinates and \stface/\ststack register them with the enclosing
group/col/row fit, so a group outline can no longer be crossed by a
member's bracket arms
- \stlink inside \stgroup or \stcol is now a package error: sub-flow
members never terminate a pending connector, so the arrow was dropped
silently while the label still rendered
- \stgroup requires role= (explicit role=neutral for mixed groups) and
must bind at least two members or one \stcol partition; a lone stack
or face inside a group is a dirty-build warning
- lint: default budget is one \stcallout per figure; the
allow-multiple-callouts directive relaxes it to one per band
- build.sh: clean-build hint no longer names hue budget (lint owns it)
- tests/group-callout.tex reworked: multi-member group with a bracketed
member as a regression probe, single callout; new negative fixtures
group-link, group-norole, group-single, callout-budget
- api.md, checklist.md, style.md, layout.md, SKILL.md updated to match
- 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
Figures were positioned by hand-written offsets. Every gap was a magic
number tuned against the content that happened to be there, so a label
that grew two characters landed on the next tensor, and two stages
started from two different x shared no rail. Both failures compile
cleanly.
Replace it with a cursor. Objects placed with an empty coordinate
argument reserve their own width -- including a stack's offset sheets
and a bracket's overhang -- and gaps are declared once (\stgutter,
\strowgap, \stblockgap). The gap belongs to the object that follows it
and the first object in a band gets none, so every band starts flush on
a shared rail and gap=0pt states that two shards tile exactly.
\stlink makes a connector's label a flow object, which is what removes
the label-wider-than-its-arrow failure entirely. \stcol is a vertical
sub-flow for a split along the contracted axis. \strow declares its
height, so an object that does not fit -- or a column that does not add
up to what it declared, and is therefore drawn off-center -- becomes a
package warning, which build.sh fails on.
Absolute placement is unchanged: passing a coordinate takes the original
code path, and \sttrack folds a hand-placed node back into the cursor.
All three golden examples and the new tests/flow.tex are converted and
build clean.
Extracted from the tensor-formula-viz skill and rebuilt around the idea that
the geometry rules should be enforced by construction rather than restated as
prose an agent has to remember.
- assets/supertensor.sty: faces, stacks, index faces, shared caption lanes,
meaning box, signature. Macros take a declared axis and a declared role, so
equal shapes get equal edges, a x a is square, a transpose swaps the face,
and contracted axes share an edge length -- without any manual alignment.
- scripts/preflight.sh: decide the TikZ/CJK path before drawing.
- scripts/build.sh: compile and fail on silent corruption (missing CJK glyphs,
overfull boxes, undeclared roles), then export pdf/svg/png/thumb.
- scripts/test.sh: build every figure as a regression test for the package.
- examples/: three golden figures (TP-FFN, causal MHA, MoE top-k gather) plus
an anti-pattern gallery of figures that compile cleanly and still lie.
- SKILL.md + references/: lean entry point, details loaded on demand.