mathfmt-tex 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mathfmt_tex-0.1.0/.github/workflows/wheels.yml +125 -0
- mathfmt_tex-0.1.0/.gitignore +9 -0
- mathfmt_tex-0.1.0/.phalanix/rig.nix +123 -0
- mathfmt_tex-0.1.0/.phalanix/roles/lead.md +46 -0
- mathfmt_tex-0.1.0/.phalanix/roles/question.md +22 -0
- mathfmt_tex-0.1.0/.phalanix/roles/review.md +41 -0
- mathfmt_tex-0.1.0/.phalanix/roles/task.md +41 -0
- mathfmt_tex-0.1.0/.phalanix/roles/traits/reports.md +37 -0
- mathfmt_tex-0.1.0/.pre-commit-hooks.yaml +29 -0
- mathfmt_tex-0.1.0/CLAUDE.md +143 -0
- mathfmt_tex-0.1.0/Cargo.lock +457 -0
- mathfmt_tex-0.1.0/Cargo.toml +44 -0
- mathfmt_tex-0.1.0/PKG-INFO +401 -0
- mathfmt_tex-0.1.0/README.md +387 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/Cargo.toml +15 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/alphabets.tsv +1023 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/defined.tsv +3001 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/greek.tsv +44 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/latex.tsv +336 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/packages.tsv +402 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/signatures.tsv +185 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/data/unicode-math.tsv +2450 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/atom.rs +150 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/config.rs +535 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/declare.rs +128 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/diag.rs +143 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/lex.rs +231 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/lib.rs +980 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/macros.rs +222 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/notation.rs +1044 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/parse.rs +1283 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/print.rs +826 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/project.rs +919 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/resolve.rs +710 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/signatures.rs +158 -0
- mathfmt_tex-0.1.0/crates/mathfmt-core/src/table.rs +481 -0
- mathfmt_tex-0.1.0/docs/REVIEW-2026-11-09.md +184 -0
- mathfmt_tex-0.1.0/docs/superpowers/plans/2026-11-05-mathfmt-v0.md +126 -0
- mathfmt_tex-0.1.0/docs/superpowers/specs/2026-11-05-mathfmt-design.md +1135 -0
- mathfmt_tex-0.1.0/flake.lock +27 -0
- mathfmt_tex-0.1.0/flake.nix +240 -0
- mathfmt_tex-0.1.0/pyproject.toml +34 -0
- mathfmt_tex-0.1.0/src/main.rs +895 -0
- mathfmt_tex-0.1.0/src/verify.rs +1047 -0
- mathfmt_tex-0.1.0/tests/cli.rs +648 -0
- mathfmt_tex-0.1.0/tests/dev.sh +40 -0
- mathfmt_tex-0.1.0/tests/fixtures/amsfonts-stubs/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/amsfonts-stubs/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/amsfonts-stubs/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/basic-to-ascii/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/basic-to-ascii/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/basic-to-ascii/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/basic-to-unicode/expected.tex +10 -0
- mathfmt_tex-0.1.0/tests/fixtures/basic-to-unicode/input.tex +10 -0
- mathfmt_tex-0.1.0/tests/fixtures/bb-lowercase/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/bb-lowercase/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/bb-lowercase-ascii/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/bb-lowercase-ascii/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/bb-lowercase-ascii/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/bf-greek-pdflatex/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/bf-greek-pdflatex/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/bf-greek-pdflatex/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/definitions-untouched/expected.tex +3 -0
- mathfmt_tex-0.1.0/tests/fixtures/definitions-untouched/input.tex +3 -0
- mathfmt_tex-0.1.0/tests/fixtures/ensuremath-macro/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/ensuremath-macro/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/group-as-script-operand/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/group-as-script-operand/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-folded/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-folded/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-folded/mathfmt.toml +8 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-inlined/defs.sty +6 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-inlined/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-inlined/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/macros-inlined/mathfmt.toml +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/no-latex-spelling/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/no-latex-spelling/expected.stderr +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/no-latex-spelling/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/no-latex-spelling/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/no-latex-spelling/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-braced-literal/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-braced-literal/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-braced-literal/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-braced-literal/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-comment-in-match/expected.tex +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-comment-in-match/input.tex +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-comment-in-match/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-form-keeps-its-macro/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-form-keeps-its-macro/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-form-keeps-its-macro/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-after-control-word/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-after-control-word/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-after-control-word/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-control-space/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-control-space/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-control-space/input.tex +3 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-hole-control-space/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-nested-delimiters/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-nested-delimiters/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-nested-delimiters/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-nested-delimiters/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-hole/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-hole/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-hole/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-operand/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-operand/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-operand/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-operand-newline/expected.tex +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-operand-newline/input.tex +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-script-operand-newline/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-spaced-form/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-spaced-form/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-spaced-form/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-spaced-form/mathfmt.toml +9 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-standing-group-in-hole/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-standing-group-in-hole/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-standing-group-in-hole/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-standing-group-in-hole/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-styled-literal/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-styled-literal/expected.tex +3 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-styled-literal/input.tex +3 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-styled-literal/mathfmt.toml +9 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-to-expanded/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-to-expanded/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-to-expanded/mathfmt.toml +6 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-to-macro/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-to-macro/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-to-macro/mathfmt.toml +8 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-unclosed-group/expected.stderr +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-unclosed-group/expected.tex +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-unclosed-group/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-unclosed-group/input.tex +4 -0
- mathfmt_tex-0.1.0/tests/fixtures/notation-unclosed-group/mathfmt.toml +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/operator-words-untouched/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/operator-words-untouched/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/prime-is-apostrophe/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/prime-is-apostrophe/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/rename-frak-to-cal/args +5 -0
- mathfmt_tex-0.1.0/tests/fixtures/rename-frak-to-cal/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/rename-frak-to-cal/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/rename-frak-to-cal/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/roundtrip-paper-snippet/expected.tex +6 -0
- mathfmt_tex-0.1.0/tests/fixtures/roundtrip-paper-snippet/input.tex +6 -0
- mathfmt_tex-0.1.0/tests/fixtures/roundtrip-paper-snippet/roundtrip +0 -0
- mathfmt_tex-0.1.0/tests/fixtures/script-braces/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/script-braces/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-relax/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-relax/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-script/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-script/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-script-ascii/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-script-ascii/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-script-ascii/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-script-ascii/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-sp/expected.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/structured-group-before-sp/input.tex +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-ascii/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-ascii/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-ascii/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-repairs/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-repairs/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-repairs/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/style-distribution-repairs/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/styled-runs/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/styled-runs/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/styled-runs/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/text-nesting/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/text-nesting/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/unbraced-argument/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/unbraced-argument/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/unbraced-argument-ascii/args +2 -0
- mathfmt_tex-0.1.0/tests/fixtures/unbraced-argument-ascii/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/unbraced-argument-ascii/expected.verify +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/unbraced-argument-ascii/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/url-arguments-skipped/expected.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/url-arguments-skipped/input.tex +1 -0
- mathfmt_tex-0.1.0/tests/fixtures/verbatim-skipped/expected.tex +8 -0
- mathfmt_tex-0.1.0/tests/fixtures/verbatim-skipped/input.tex +8 -0
- mathfmt_tex-0.1.0/tests/golden.rs +211 -0
- mathfmt_tex-0.1.0/tests/pre-commit-e2e.sh +50 -0
- mathfmt_tex-0.1.0/tests/project.rs +82 -0
- mathfmt_tex-0.1.0/tests/projects/bbold/expected/main.tex +6 -0
- mathfmt_tex-0.1.0/tests/projects/bbold/expected.declarations +7 -0
- mathfmt_tex-0.1.0/tests/projects/bbold/main.tex +6 -0
- mathfmt_tex-0.1.0/tests/projects/bbold/mathfmt.toml +2 -0
- mathfmt_tex-0.1.0/tests/projects/paper/expected/main.tex +8 -0
- mathfmt_tex-0.1.0/tests/projects/paper/expected/sections/method.tex +2 -0
- mathfmt_tex-0.1.0/tests/projects/paper/expected/sections/results.tex +3 -0
- mathfmt_tex-0.1.0/tests/projects/paper/expected.declarations +18 -0
- mathfmt_tex-0.1.0/tests/projects/paper/main.tex +8 -0
- mathfmt_tex-0.1.0/tests/projects/paper/mathfmt.toml +1 -0
- mathfmt_tex-0.1.0/tests/projects/paper/paper.sty +6 -0
- mathfmt_tex-0.1.0/tests/projects/paper/sections/method.tex +2 -0
- mathfmt_tex-0.1.0/tests/projects/paper/sections/results.tex +3 -0
- mathfmt_tex-0.1.0/tests/projects/unicode-math/expected/main.tex +8 -0
- mathfmt_tex-0.1.0/tests/projects/unicode-math/expected.declarations +0 -0
- mathfmt_tex-0.1.0/tests/projects/unicode-math/main.tex +8 -0
- mathfmt_tex-0.1.0/tests/projects/unicode-math/mathfmt.toml +2 -0
- mathfmt_tex-0.1.0/tests/verify.rs +608 -0
- mathfmt_tex-0.1.0/tools/gen-packages.py +241 -0
- mathfmt_tex-0.1.0/tools/gen-table.py +157 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Wheels of mathfmt-tex for every platform pre-commit users run, built on
|
|
2
|
+
# the GitHub mirror (Forgejo pushes to it). A wheel holds the compiled
|
|
3
|
+
# `mathfmt` binary only (pyproject.toml), so installing it needs no Rust.
|
|
4
|
+
# A tag `v*` also publishes to PyPI through trusted publishing: the PyPI
|
|
5
|
+
# project must name this repository and this workflow as its publisher.
|
|
6
|
+
#
|
|
7
|
+
# Every job is skipped anywhere but github.com, so a Forgejo instance that
|
|
8
|
+
# reads .github/workflows does not try to run it.
|
|
9
|
+
|
|
10
|
+
name: wheels
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
tags: ["v*"]
|
|
16
|
+
pull_request:
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
linux:
|
|
24
|
+
if: github.server_url == 'https://github.com'
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
strategy:
|
|
27
|
+
matrix:
|
|
28
|
+
target: [x86_64, aarch64]
|
|
29
|
+
libc: [manylinux, musllinux]
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- uses: PyO3/maturin-action@v1
|
|
33
|
+
with:
|
|
34
|
+
target: ${{ matrix.target }}
|
|
35
|
+
manylinux: ${{ matrix.libc == 'manylinux' && 'auto' || 'musllinux_1_2' }}
|
|
36
|
+
args: --release --locked --out dist
|
|
37
|
+
- name: Run the wheel's mathfmt
|
|
38
|
+
if: matrix.target == 'x86_64' && matrix.libc == 'manylinux'
|
|
39
|
+
run: |
|
|
40
|
+
python3 -m venv /tmp/venv
|
|
41
|
+
/tmp/venv/bin/pip install dist/*.whl
|
|
42
|
+
/tmp/venv/bin/mathfmt --version
|
|
43
|
+
printf '%s\n' '$\alpha \in \mathbb{R}$' | /tmp/venv/bin/mathfmt | grep -Fx '$α ∈ ℝ$'
|
|
44
|
+
- uses: actions/upload-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
name: wheels-linux-${{ matrix.target }}-${{ matrix.libc }}
|
|
47
|
+
path: dist
|
|
48
|
+
|
|
49
|
+
macos:
|
|
50
|
+
if: github.server_url == 'https://github.com'
|
|
51
|
+
strategy:
|
|
52
|
+
matrix:
|
|
53
|
+
include:
|
|
54
|
+
- runner: macos-15-intel
|
|
55
|
+
target: x86_64
|
|
56
|
+
- runner: macos-15
|
|
57
|
+
target: aarch64
|
|
58
|
+
runs-on: ${{ matrix.runner }}
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
- uses: PyO3/maturin-action@v1
|
|
62
|
+
with:
|
|
63
|
+
target: ${{ matrix.target }}
|
|
64
|
+
args: --release --locked --out dist
|
|
65
|
+
- name: Run the wheel's mathfmt
|
|
66
|
+
run: |
|
|
67
|
+
python3 -m venv /tmp/venv
|
|
68
|
+
/tmp/venv/bin/pip install dist/*.whl
|
|
69
|
+
/tmp/venv/bin/mathfmt --version
|
|
70
|
+
printf '%s\n' '$\alpha \in \mathbb{R}$' | /tmp/venv/bin/mathfmt | grep -Fx '$α ∈ ℝ$'
|
|
71
|
+
- uses: actions/upload-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: wheels-macos-${{ matrix.target }}
|
|
74
|
+
path: dist
|
|
75
|
+
|
|
76
|
+
windows:
|
|
77
|
+
if: github.server_url == 'https://github.com'
|
|
78
|
+
runs-on: windows-latest
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
- uses: actions/setup-python@v5
|
|
82
|
+
with:
|
|
83
|
+
python-version: "3.x"
|
|
84
|
+
- uses: PyO3/maturin-action@v1
|
|
85
|
+
with:
|
|
86
|
+
target: x64
|
|
87
|
+
args: --release --locked --out dist
|
|
88
|
+
- name: Run the wheel's mathfmt
|
|
89
|
+
shell: bash
|
|
90
|
+
run: |
|
|
91
|
+
python -m venv venv
|
|
92
|
+
venv/Scripts/pip install dist/*.whl
|
|
93
|
+
venv/Scripts/mathfmt --version
|
|
94
|
+
- uses: actions/upload-artifact@v4
|
|
95
|
+
with:
|
|
96
|
+
name: wheels-windows-x64
|
|
97
|
+
path: dist
|
|
98
|
+
|
|
99
|
+
sdist:
|
|
100
|
+
if: github.server_url == 'https://github.com'
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v4
|
|
104
|
+
- uses: PyO3/maturin-action@v1
|
|
105
|
+
with:
|
|
106
|
+
command: sdist
|
|
107
|
+
args: --out dist
|
|
108
|
+
- uses: actions/upload-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
name: sdist
|
|
111
|
+
path: dist
|
|
112
|
+
|
|
113
|
+
publish:
|
|
114
|
+
if: github.server_url == 'https://github.com' && startsWith(github.ref, 'refs/tags/v')
|
|
115
|
+
needs: [linux, macos, windows, sdist]
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
environment: pypi
|
|
118
|
+
permissions:
|
|
119
|
+
id-token: write
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/download-artifact@v4
|
|
122
|
+
with:
|
|
123
|
+
path: dist
|
|
124
|
+
merge-multiple: true
|
|
125
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# The mathfmt rig: seats that develop mathfmt, in the loop that built
|
|
2
|
+
# phalanix v0.6.0. Bring it up from a checkout with
|
|
3
|
+
#
|
|
4
|
+
# phalanix up .phalanix/rig.nix
|
|
5
|
+
#
|
|
6
|
+
# `up` sends this one file to compute, the seat host, and evaluates it there
|
|
7
|
+
# (restricted: no imports, no reads), passing `phx` (phalanix's nix/phx.nix).
|
|
8
|
+
#
|
|
9
|
+
# Standing:
|
|
10
|
+
#
|
|
11
|
+
# lead one wave at a time: its spec, its plan and its ledger of
|
|
12
|
+
# rulings (docs/handoff/wave-NN.md); starts task, review and
|
|
13
|
+
# question seats with briefs, reads their summaries, chains
|
|
14
|
+
# tasks with --base, and tells the person what to land
|
|
15
|
+
#
|
|
16
|
+
# Templates, each spawned by lead (or the person) with a brief:
|
|
17
|
+
#
|
|
18
|
+
# task one task of the plan, test first, on its own branch
|
|
19
|
+
# review an adversarial review of one branch against its base;
|
|
20
|
+
# reports findings and never fixes them
|
|
21
|
+
# question one question about the code, answered
|
|
22
|
+
#
|
|
23
|
+
# Every kind runs on the strongest model, the person's rule for multi-agent
|
|
24
|
+
# work. Roles are repo: layers read from each seat's branch at every start,
|
|
25
|
+
# so .phalanix/ must be on main before the first seat starts. CLAUDE.md at
|
|
26
|
+
# the root is every seat's memory too: it holds the build, the tests and the
|
|
27
|
+
# person's rules, and the roles do not repeat it.
|
|
28
|
+
#
|
|
29
|
+
# The repository is jack/mathfmt on Forgejo, which is private: compute's
|
|
30
|
+
# daemon clones it and pushes submitted branches with the deploy key compute
|
|
31
|
+
# holds (secrets/phalanix-forge-forgejo in ~/dev/nix), which must be
|
|
32
|
+
# registered on the repository with write access. compute admits every seat
|
|
33
|
+
# user to its main nix daemon (nix.settings.allowed-users in ~/dev/nix). The
|
|
34
|
+
# dev shell vendors every crate from Cargo.lock (flake.nix, tests/dev.sh), so
|
|
35
|
+
# the seat proxy needs no crates.io entry: the nix daemon fetches the crates,
|
|
36
|
+
# and github.com with codeload (already allowed) serves the pinned nixpkgs
|
|
37
|
+
# when compute's store lacks it. Needs phalanix v0.6.0 or later.
|
|
38
|
+
{ host ? { }, lib, phx, ... }:
|
|
39
|
+
let
|
|
40
|
+
repo = "ssh://forgejo@git.buttermilks.rocks/jack/mathfmt.git";
|
|
41
|
+
layer = path: "repo:.phalanix/roles/${path}.md";
|
|
42
|
+
|
|
43
|
+
# What every seat and template shares, given as each kind's own options.
|
|
44
|
+
common = {
|
|
45
|
+
inherit repo;
|
|
46
|
+
tool = "claude";
|
|
47
|
+
base = "main";
|
|
48
|
+
continuity = "resume";
|
|
49
|
+
model = "opus";
|
|
50
|
+
};
|
|
51
|
+
kind = traits: own: phx.kind traits (common // own);
|
|
52
|
+
|
|
53
|
+
# A seat's PATH may have no nix or bash; the roles and CLAUDE.md spell it out.
|
|
54
|
+
nix = "/run/current-system/sw/bin/nix";
|
|
55
|
+
bash = "/run/current-system/sw/bin/bash";
|
|
56
|
+
|
|
57
|
+
traits = {
|
|
58
|
+
reports = phx.trait { role = layer "traits/reports"; };
|
|
59
|
+
# The dev shell through tests/dev.sh (cargo against the vendored crates,
|
|
60
|
+
# clippy, rustfmt; the wrapper drops the seat's identity first), and the
|
|
61
|
+
# flake checks, which the nix daemon builds. This is a blanket
|
|
62
|
+
# pre-approval: anything wrapped in dev.sh skips the permission
|
|
63
|
+
# classifier, and dev.sh is a file the seat may edit on its own branch.
|
|
64
|
+
# Accepted, because building and testing is the seat's whole job. The
|
|
65
|
+
# second form is for a seat whose PATH has no bash for dev.sh's shebang.
|
|
66
|
+
builds = phx.trait {
|
|
67
|
+
permissions.allow = [
|
|
68
|
+
"Bash(repo/tests/dev.sh *)"
|
|
69
|
+
"Bash(${bash} repo/tests/dev.sh *)"
|
|
70
|
+
"Bash(${nix} build *)"
|
|
71
|
+
"Bash(${nix} flake check *)"
|
|
72
|
+
];
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
rig = phx.merge [
|
|
77
|
+
{
|
|
78
|
+
seats = {
|
|
79
|
+
# lead reads summaries, not logs, so it does not build.
|
|
80
|
+
lead = kind [ ] {
|
|
81
|
+
role = layer "lead";
|
|
82
|
+
memory = "8G";
|
|
83
|
+
accept = "ask";
|
|
84
|
+
spawns = { task = 3; review = 2; question = 2; };
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
templates = {
|
|
88
|
+
task = kind [ traits.reports traits.builds ] {
|
|
89
|
+
role = layer "task";
|
|
90
|
+
memory = "8G";
|
|
91
|
+
accept = "auto";
|
|
92
|
+
spawns.question = 1;
|
|
93
|
+
};
|
|
94
|
+
review = kind [ traits.reports traits.builds ] {
|
|
95
|
+
role = layer "review";
|
|
96
|
+
memory = "8G";
|
|
97
|
+
accept = "auto";
|
|
98
|
+
};
|
|
99
|
+
question = kind [ traits.reports traits.builds ] {
|
|
100
|
+
role = layer "question";
|
|
101
|
+
memory = "8G";
|
|
102
|
+
accept = "auto";
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
kinds = rig.seats // rig.templates;
|
|
109
|
+
both = a: b: [{ from = a; to = b; } { from = b; to = a; }];
|
|
110
|
+
in
|
|
111
|
+
rig // {
|
|
112
|
+
name = "mathfmt";
|
|
113
|
+
|
|
114
|
+
# Every seat: the verbs, and git in its clone (./repo).
|
|
115
|
+
permissions.allow = [ "Bash(phalanix *)" "Bash(git -C repo *)" ];
|
|
116
|
+
|
|
117
|
+
# lead talks to every other kind, both ways. A parent and its child reach
|
|
118
|
+
# each other without an edge, so a task's question needs none.
|
|
119
|
+
edges = rig.edges
|
|
120
|
+
++ lib.concatMap (both "lead") (lib.attrNames (removeAttrs kinds [ "lead" ]));
|
|
121
|
+
|
|
122
|
+
commit.author_email = "mathfmt@phalanix.invalid";
|
|
123
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
You are lead, the mathfmt rig's controller. You run one wave of mathfmt development at a
|
|
2
|
+
time: you hold its spec, its plan and its ledger, and nothing below them. Each seat you start
|
|
3
|
+
holds less than you do, and more detail. CLAUDE.md (in your memory) has the build, the tests
|
|
4
|
+
and the person's rules.
|
|
5
|
+
|
|
6
|
+
**Each life.** Read your ledger, docs/handoff/wave-NN.md on your branch, before anything
|
|
7
|
+
else; it is your memory across lives, and the transcript is not. In your first life there is
|
|
8
|
+
no ledger yet: read docs/superpowers/ and ask the person what the first wave is. `phalanix
|
|
9
|
+
items --mine` and `phalanix inbox --ack` show what arrived while you were away.
|
|
10
|
+
|
|
11
|
+
**A wave** lands as one chain of branches. Each seat starts from the branch of the seat
|
|
12
|
+
before it, so the last seat's branch holds the whole wave. Run it in order:
|
|
13
|
+
|
|
14
|
+
1. **Spec.** Talk the design through with the person in your pane. Write it as
|
|
15
|
+
docs/superpowers/specs/<date>-<topic>-design.md, or a dated amendment at the end of the
|
|
16
|
+
existing one, and get their approval.
|
|
17
|
+
2. **Plan.** Write docs/superpowers/plans/<date>-<topic>.md: the tasks, each small enough for
|
|
18
|
+
one seat and one review, with the files it touches, the interfaces it produces and the
|
|
19
|
+
fixtures and tests that prove it. Tasks that share a file or an interface must agree on
|
|
20
|
+
it. Get the person's approval.
|
|
21
|
+
3. **Land the spec and plan.** Start the ledger, docs/handoff/wave-NN.md (the next number):
|
|
22
|
+
the plan's path, then one line per event and every ruling. Commit, `phalanix file "wave
|
|
23
|
+
NN: spec and plan"`, `phalanix claim <item>`, `phalanix submit <item>`. Ask the person to
|
|
24
|
+
land your branch (`phalanix land mathfmt/lead`) and push main; then `phalanix sync --dir
|
|
25
|
+
repo` and check that origin/main has the plan. From here on your branch changes only the
|
|
26
|
+
ledger.
|
|
27
|
+
4. **Tasks**, one at a time, in plan order. Write a brief file holding the task's full text,
|
|
28
|
+
the interfaces earlier tasks produced, your rulings, and what to report; `phalanix spawn
|
|
29
|
+
--template task --brief-file <file> --no-attach`, with `--base seats/mathfmt/<previous
|
|
30
|
+
task>` for every task after the first, and only once `phalanix items --json` shows that
|
|
31
|
+
seat's latest item done with `pushed_at` set. Then a review with `--base
|
|
32
|
+
seats/mathfmt/<task>` and a brief naming the task's brief, its report, the commit range,
|
|
33
|
+
the risks you see, the spec and plan. Rule on each finding; `phalanix close
|
|
34
|
+
<review>@mathfmt`. Findings go back as a new item (`phalanix file "fix round <r> for
|
|
35
|
+
<task>: ..."`, `phalanix offer <item> <task>@mathfmt`), at most five rounds. Copy each
|
|
36
|
+
task's counts and open points from its report into the ledger (attachments are deleted 14
|
|
37
|
+
days after close), then `phalanix close <task>@mathfmt`. A finding about a task a later
|
|
38
|
+
task has built on goes to a new task at the tip of the chain, never back down it.
|
|
39
|
+
5. **Final review.** One review seat over the whole wave, main to the last branch; its brief
|
|
40
|
+
says it commits docs/REVIEW-<date>-waveNN.md and submits. Its findings go to a fix task.
|
|
41
|
+
6. **Verification.** The last seat runs `repo/tests/dev.sh cargo test --workspace`, clippy,
|
|
42
|
+
`cargo fmt --check` and every flake check, writes the counts into the ledger, and submits.
|
|
43
|
+
Tell the person what to land, in order.
|
|
44
|
+
|
|
45
|
+
**Rulings** are yours (an ambiguity in the plan, a finding to leave, a test a task seat says
|
|
46
|
+
is wrong), one line each in the ledger. What you cannot rule on goes to the person.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
You are a question seat of the mathfmt rig. You investigate one question from your brief,
|
|
2
|
+
answer it and stop. CLAUDE.md (in your memory) has the build, the tests and the person's
|
|
3
|
+
rules; the reports layer above says how to report.
|
|
4
|
+
|
|
5
|
+
**Your scope** is the question and the evidence for its answer. That covers the code, the
|
|
6
|
+
data tables, the fixtures, docs/ (the spec, the plans, the reviews, the handoffs), and small
|
|
7
|
+
checks in your clone, ./repo: git as `git -C repo ...`, anything else through
|
|
8
|
+
`repo/tests/dev.sh <command>` (a one-off `repo/tests/dev.sh cargo run -- --emit atoms` on a
|
|
9
|
+
scratch file is the usual check). You answer your parent, the seat that spawned you; when the
|
|
10
|
+
person spawned you, you answer lead. **You never carry** the wave's plan, another seat's
|
|
11
|
+
change or its review: what you need of your parent's context is in your brief, and anything
|
|
12
|
+
more you ask your parent for. You never change code beyond a scratch check you do not commit,
|
|
13
|
+
and you never spawn a seat.
|
|
14
|
+
|
|
15
|
+
**Answer** in the form your brief asks for, the answer first. Back it with file:line
|
|
16
|
+
references, or with a command and what it printed.
|
|
17
|
+
- A short answer is a message to your parent: `phalanix send <parent> "<answer>" --item
|
|
18
|
+
<item>`, then `phalanix done <item> --reason completed`.
|
|
19
|
+
- A longer one is a `report.md` attachment, as the reports layer says.
|
|
20
|
+
- When only a change or a long run can answer the question, say which one would, and stop.
|
|
21
|
+
|
|
22
|
+
**Commits.** You make none.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
You are a review seat of the mathfmt rig: an adversarial reviewer of one branch. Your clone
|
|
2
|
+
starts on the branch under review (your spawn's base). Your brief names the commit range, the
|
|
3
|
+
task's brief and report, the wave's spec and plan, and the risks your parent sees. CLAUDE.md
|
|
4
|
+
(in your memory) has the build, the tests and the person's rules; the reports layer above says
|
|
5
|
+
how to report.
|
|
6
|
+
|
|
7
|
+
**Your scope** is the range in your brief and what it touches. **You never fix anything.** You
|
|
8
|
+
never commit code, push a fix or spawn a seat. You find what is wrong and show it; the task
|
|
9
|
+
seat fixes it, and your parent rules on what not to fix.
|
|
10
|
+
|
|
11
|
+
**Review** your clone, ./repo, against two questions. Run git as `git -C repo ...`, and any
|
|
12
|
+
build or test through the wrapper as `repo/tests/dev.sh <command>` (CLAUDE.md). The spec and
|
|
13
|
+
plan are on main, so they are in your clone too.
|
|
14
|
+
1. **Does it do what the brief, spec and plan say?** Every requirement met, nothing missing,
|
|
15
|
+
nothing extra. A moved interface named in the report.
|
|
16
|
+
2. **Is it right and maintainable?**
|
|
17
|
+
- Round trips: does the output go back through the other profile to the input? Is the
|
|
18
|
+
output idempotent? Are untouched bytes byte-identical?
|
|
19
|
+
- Edge inputs: `\$`, `\\(`, `%` inside display math, `$` inside `\text{}` inside `$…$`,
|
|
20
|
+
`\verb|$|`, a macro that expands to itself, an empty file, a file with no math.
|
|
21
|
+
- Tables, not code: a symbol added as a match arm instead of a data row.
|
|
22
|
+
- Tests that would fail if the code were wrong; a fixture whose expected.tex was copied
|
|
23
|
+
from the output without being read counts as no test.
|
|
24
|
+
- Readable by one person: names, comments and prose that say why.
|
|
25
|
+
|
|
26
|
+
**Prove what you claim.** Read the code at the lines you cite. Where a finding can be shown,
|
|
27
|
+
show it: a failing test or fixture you write in a scratch file (not committed), a command and
|
|
28
|
+
its output, or an input traced through the code. Separate CONFIRMED findings, which you have
|
|
29
|
+
shown, from PLAUSIBLE ones, which you have reasoned but not shown, and drop what you could
|
|
30
|
+
not support.
|
|
31
|
+
|
|
32
|
+
**Report.** Rank findings High, Medium or Low. Each gets the file and line; what is wrong, as
|
|
33
|
+
one sentence; the input or state that breaks it, and what happens; the fix you would expect.
|
|
34
|
+
End with a verdict: approve, or needs fixes. For a task or a fix round, the report is the
|
|
35
|
+
attachment the reports layer describes, and you finish with `phalanix done <item> --reason
|
|
36
|
+
completed`. For a whole wave, your brief says so: write the review as
|
|
37
|
+
docs/REVIEW-<date>-waveNN.md, commit it, attach the summary, and `phalanix submit <item>`.
|
|
38
|
+
|
|
39
|
+
**Commits** (the review document only). `area: what changed`, lowercase, a title of 60
|
|
40
|
+
characters or fewer, a body that says why, no model identifier, hooks never skipped. phalanix
|
|
41
|
+
sets your identity: never change git config. You never push.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
You are a task seat of the mathfmt rig. You implement one task of a wave's plan, from the
|
|
2
|
+
brief your parent gave you, on your own branch. CLAUDE.md (in your memory) has the build, the
|
|
3
|
+
tests and the person's rules; the reports layer above says how to report.
|
|
4
|
+
|
|
5
|
+
**Your scope** is your task: its code, tests, fixtures, docs and runs. **You never carry**
|
|
6
|
+
the wave's plan beyond your task (lead's), another task's change, or a review of your own
|
|
7
|
+
work: a review seat does that after you report, and you never start one. The brief holds what
|
|
8
|
+
you need of the spec and plan; read the files it names, and ask your parent rather than guess.
|
|
9
|
+
|
|
10
|
+
**Work** in your clone, ./repo. Run git as `git -C repo ...`, and every build and test
|
|
11
|
+
through the wrapper as `repo/tests/dev.sh <command>` (CLAUDE.md says why; never a bare `nix
|
|
12
|
+
develop`; cargo runs offline against the vendored crates, so a new dependency needs
|
|
13
|
+
Cargo.lock committed before the shell sees it).
|
|
14
|
+
- Test first: write the test, or the fixture under tests/fixtures/<case>/, that pins the
|
|
15
|
+
brief's behaviour, watch it fail, make it pass. A bug you find on the way becomes a fixture.
|
|
16
|
+
- A symbol, an alias or a command signature is a row in crates/mathfmt-core/data/latex.tsv
|
|
17
|
+
or signatures.tsv, never a match arm; unicode-math.tsv and alphabets.tsv are generated by
|
|
18
|
+
tools/gen-table.py and edited only by rerunning it.
|
|
19
|
+
- Match the code and the prose around you: module boundaries from the spec, the fixture
|
|
20
|
+
layout, the comment density. When your change moves an interface, say so in the report.
|
|
21
|
+
- Before you report, run `repo/tests/dev.sh cargo test --workspace`, `repo/tests/dev.sh
|
|
22
|
+
cargo clippy --workspace --all-targets -- -D warnings` and `repo/tests/dev.sh cargo fmt
|
|
23
|
+
--check`. A flake check (`/run/current-system/sw/bin/nix build
|
|
24
|
+
./repo#checks.x86_64-linux.<name>`) only when your change touches flake.nix or Cargo.lock.
|
|
25
|
+
- Never weaken a test or edit an expected.tex to make it pass. When a test is wrong, say why
|
|
26
|
+
in the report.
|
|
27
|
+
|
|
28
|
+
**Fix rounds.** After your report, a fix round may arrive as a new item, naming findings and
|
|
29
|
+
your parent's rulings on them. Fix exactly those findings, with tests where they apply,
|
|
30
|
+
commit, report on that item, and submit it. Don't reopen what the round did not name.
|
|
31
|
+
|
|
32
|
+
**Help.** You may have one question seat live, spawned with a brief that says the question
|
|
33
|
+
and the form of answer you need: `phalanix spawn --template question --brief-file <file>
|
|
34
|
+
--no-attach`.
|
|
35
|
+
|
|
36
|
+
**Result.** Your branch carries the change, its tests and the docs it moves, in commits that
|
|
37
|
+
each build. Report as the reports layer says, then `phalanix submit <item>`.
|
|
38
|
+
|
|
39
|
+
**Commits.** `area: what changed`, lowercase, a title of 60 characters or fewer and a body
|
|
40
|
+
that says why; no model identifier anywhere; never skip hooks. phalanix sets your identity:
|
|
41
|
+
never change git config. You never push; the daemon pushes your branch when you submit.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
How you report. You hold one piece of work, and its details go to exactly one seat: your parent.
|
|
2
|
+
|
|
3
|
+
**Your parent** is the seat that spawned you. `phalanix ps --all` shows it in the PARENT
|
|
4
|
+
column, and your brief item's ref names the item it holds (`item:<n>`) or the seat itself. A
|
|
5
|
+
seat the person spawned has no parent, and reports to lead. Parent and child reach each other
|
|
6
|
+
without an edge.
|
|
7
|
+
|
|
8
|
+
**Start.** `phalanix items --mine`. Your brief, and any later fix round, is usually yours
|
|
9
|
+
already: you accept automatically, so the ledger has taken it for you. `phalanix accept
|
|
10
|
+
<item>` only an item still shown as offered (`claim` takes only an open item no one was
|
|
11
|
+
offered). Read the brief whole. If it is unclear, ask your parent before you change
|
|
12
|
+
anything.
|
|
13
|
+
|
|
14
|
+
**Details go in a report attachment.** Write `report.md`:
|
|
15
|
+
- what you did;
|
|
16
|
+
- the commits, as `git log --oneline` shows them;
|
|
17
|
+
- what you ran and its counts, as cargo prints them (`test result: ok. N passed`);
|
|
18
|
+
- anything you doubted or left open.
|
|
19
|
+
Attach it: `phalanix attach <item> report.md --name report-<n>.md`, with `<n>` the item
|
|
20
|
+
number, since an item holds one attachment per name.
|
|
21
|
+
|
|
22
|
+
**Finishing.** Attach first: `done` is final, and nothing can be attached after it. Then:
|
|
23
|
+
- When your branch carries the result, `phalanix submit <item>`. The daemon pushes it soon
|
|
24
|
+
after, as seats/mathfmt/<your seat>, and nothing leaves your clone before.
|
|
25
|
+
- When the result is attachments and messages only, `phalanix done <item> --reason
|
|
26
|
+
completed`.
|
|
27
|
+
- When you are stuck, `phalanix done <item> --reason blocked-on --target item:<n>`, or
|
|
28
|
+
`--reason escalated --target person` when only the person can unblock you.
|
|
29
|
+
- When it is not your work after all, attach what you have and hand the item back:
|
|
30
|
+
`phalanix handoff <item> <parent>`.
|
|
31
|
+
Then tell your parent in a line or two: `phalanix send <parent> "<what is ready>" --item
|
|
32
|
+
<item>`. A message names things; it never carries a log, a diff or a table. Read mail with
|
|
33
|
+
`phalanix inbox --ack`.
|
|
34
|
+
|
|
35
|
+
**Your own children**, if you spawn any, report to you the same way. Read their reports,
|
|
36
|
+
fetch only the attachment you need (`phalanix fetch <item> <name>`), and once a child's item is
|
|
37
|
+
done, `phalanix close <child>@mathfmt` to free its slot.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Use this repository as a pre-commit hook:
|
|
2
|
+
#
|
|
3
|
+
# repos:
|
|
4
|
+
# - repo: ssh://forgejo@git.buttermilks.rocks/jack/mathfmt.git
|
|
5
|
+
# rev: v0.1.0
|
|
6
|
+
# hooks:
|
|
7
|
+
# - id: mathfmt # rewrites staged .tex/.sty files to the profile
|
|
8
|
+
# # - id: mathfmt-check # or: fail the commit, change nothing
|
|
9
|
+
#
|
|
10
|
+
# `language: rust` builds the binary with cargo on first use. An installed
|
|
11
|
+
# binary on PATH works the same way with `language: system` in your own
|
|
12
|
+
# .pre-commit-config.yaml.
|
|
13
|
+
- id: mathfmt
|
|
14
|
+
name: mathfmt
|
|
15
|
+
description: enforce math-notation conventions in LaTeX source
|
|
16
|
+
entry: mathfmt
|
|
17
|
+
language: rust
|
|
18
|
+
args: ["--write"]
|
|
19
|
+
files: \.(tex|sty)$
|
|
20
|
+
pass_filenames: true
|
|
21
|
+
|
|
22
|
+
- id: mathfmt-check
|
|
23
|
+
name: mathfmt (check)
|
|
24
|
+
description: fail when math notation does not match the profile; change nothing
|
|
25
|
+
entry: mathfmt
|
|
26
|
+
language: rust
|
|
27
|
+
args: ["--check"]
|
|
28
|
+
files: \.(tex|sty)$
|
|
29
|
+
pass_filenames: true
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# mathfmt
|
|
2
|
+
|
|
3
|
+
A filter that rewrites the math notation in LaTeX source to a configured
|
|
4
|
+
profile: many spellings in, one spelling out, nothing else touched. The design
|
|
5
|
+
is `docs/superpowers/specs/2026-11-05-mathfmt-design.md`; this file is how to
|
|
6
|
+
work in the repository. It is loaded by every seat of the mathfmt rig
|
|
7
|
+
(`.phalanix/rig.nix`) and by anyone else working here.
|
|
8
|
+
|
|
9
|
+
## Where things are
|
|
10
|
+
|
|
11
|
+
- `docs/superpowers/specs/`: the design, one dated file; amendments go at its
|
|
12
|
+
end, dated. `docs/superpowers/plans/`: one plan per wave, a task per seat.
|
|
13
|
+
- `docs/handoff/wave-NN.md`: lead's ledger for a wave (rulings, one line per
|
|
14
|
+
event, a paragraph per task copied from its report). `docs/REVIEW-*.md`: a
|
|
15
|
+
wave's adversarial review.
|
|
16
|
+
- `crates/mathfmt-core/`: the library, one module per pipeline stage (scan,
|
|
17
|
+
lex, parse, macros, notation, resolve, profile, print, config, diag).
|
|
18
|
+
`notation.rs` holds the pattern matcher and the rewrite for notations with
|
|
19
|
+
holes (`[notations]` in the config). `project.rs` reads a root document
|
|
20
|
+
(the files it inputs, the packages and local definition files of its
|
|
21
|
+
preamble, the engine) and `declare.rs` holds what `mathfmt declarations`
|
|
22
|
+
prints. `src/main.rs`:
|
|
23
|
+
the CLI, thin; it is the root package so that pre-commit's
|
|
24
|
+
`cargo install --path .` works.
|
|
25
|
+
- `crates/mathfmt-core/data/`: the tables. `unicode-math.tsv`,
|
|
26
|
+
`alphabets.tsv` and `packages.tsv` are generated; `latex.tsv` and
|
|
27
|
+
`signatures.tsv` are written by hand and are where a symbol, an alias or a
|
|
28
|
+
command signature is added.
|
|
29
|
+
- `tools/gen-table.py`: regenerates the two Unicode tables from
|
|
30
|
+
`kpsewhich unicode-math-table.tex`. `tools/gen-packages.py`: regenerates
|
|
31
|
+
`packages.tsv` by asking pdflatex and lualatex which package defines each
|
|
32
|
+
command the tables emit; run it after adding a command to `latex.tsv` or a
|
|
33
|
+
style command to `signatures.tsv` (a unit test fails until it is).
|
|
34
|
+
- `tests/fixtures/<case>/`: the golden cases, run by `tests/golden.rs` and
|
|
35
|
+
compiled and compared by `tests/verify.rs`; `tests/cli.rs`: the binary's
|
|
36
|
+
interface. `tests/projects/<case>/`: small document trees with a
|
|
37
|
+
`mathfmt.toml` naming their `root`, converted and compared with
|
|
38
|
+
`expected/` by `tests/project.rs` (and `expected.declarations`), and
|
|
39
|
+
compiled by `tests/verify.rs` with `--project --verify --declare`.
|
|
40
|
+
`tests/dev.sh`: the dev shell wrapper. `flake.nix`: dev shell,
|
|
41
|
+
package and checks. `src/verify.rs`: the `--verify` mode (compile both
|
|
42
|
+
sides, rasterize, compare).
|
|
43
|
+
- `pyproject.toml`: the Python distribution `mathfmt-tex`, a wheel holding
|
|
44
|
+
the binary only (maturin, `bindings = "bin"`); `.github/workflows/wheels.yml`
|
|
45
|
+
builds it per platform on the GitHub mirror and publishes from a `v*` tag;
|
|
46
|
+
`tests/pre-commit-e2e.sh` runs it as a pre-commit hook by hand.
|
|
47
|
+
- `.phalanix/`: the rig (lead, and task/review/question templates) and its
|
|
48
|
+
roles, for waves run on phalanix.
|
|
49
|
+
|
|
50
|
+
## Building and testing
|
|
51
|
+
|
|
52
|
+
On a laptop with network, plain `cargo test --workspace` works. Everywhere
|
|
53
|
+
else, and always in a seat, go through the wrapper, which enters the dev shell
|
|
54
|
+
from the repository root and runs cargo offline against the crates nix vendored
|
|
55
|
+
from `Cargo.lock`:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
tests/dev.sh cargo test --workspace
|
|
59
|
+
tests/dev.sh cargo clippy --workspace --all-targets -- -D warnings
|
|
60
|
+
tests/dev.sh cargo fmt --check
|
|
61
|
+
repo/tests/dev.sh cargo test --workspace # from a seat's state directory
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Never use a bare `nix develop` in a seat: the wrapper drops every `PHALANIX_*`
|
|
65
|
+
and `PG*` variable first (a phalanix binary that sees them dials the live bus
|
|
66
|
+
as the seat), puts `nix` on PATH (a seat's PATH has none) and runs from the
|
|
67
|
+
root. If a seat's PATH has no bash for the shebang, run
|
|
68
|
+
`/run/current-system/sw/bin/bash repo/tests/dev.sh <command>` and report it.
|
|
69
|
+
|
|
70
|
+
- The dev shell writes `.cargo-home/config.toml` (git-ignored), pointing
|
|
71
|
+
cargo at the vendored crates and setting it offline, so `cargo`,
|
|
72
|
+
`cargo clippy` and rust-analyzer all build without network.
|
|
73
|
+
- Nix sees only what git tracks. Commit, or at least `git add`, before any
|
|
74
|
+
`nix build`, `nix flake check` or a wrapper run that needs a new file; a new
|
|
75
|
+
dependency reaches the shell once the updated `Cargo.lock` is added.
|
|
76
|
+
- `nix build` builds the binary and runs the tests. The flake checks are
|
|
77
|
+
`build`, `clippy`, `fmt`, `verify` and `wheel` (the Python wheel builds
|
|
78
|
+
offline and its `mathfmt` runs): `nix build .#checks.x86_64-linux.<name>`,
|
|
79
|
+
or in a seat `/run/current-system/sw/bin/nix build ./repo#checks.x86_64-linux.<name>`.
|
|
80
|
+
`verify` brings a TeX Live and runs `tests/verify.rs` with
|
|
81
|
+
`MATHFMT_VERIFY_REQUIRED` set; elsewhere that test skips itself when
|
|
82
|
+
`pdflatex` or `pdftoppm` is missing (the dev shell on a machine with TeX
|
|
83
|
+
Live has them on PATH, so `cargo test` runs it).
|
|
84
|
+
- Dependencies are what `Cargo.toml` lists; a new one is a decision for the
|
|
85
|
+
plan, not a convenience.
|
|
86
|
+
|
|
87
|
+
A wave is verified when the tests, clippy, fmt and every flake check pass on its
|
|
88
|
+
last commit, and the ledger records the counts.
|
|
89
|
+
|
|
90
|
+
## Fixtures
|
|
91
|
+
|
|
92
|
+
A golden case is a directory `tests/fixtures/<case>/` holding `input.tex`,
|
|
93
|
+
`expected.tex`, and optionally `mathfmt.toml` (the config; the defaults
|
|
94
|
+
otherwise), `expected.stderr` (the diagnostics) and `args` (one CLI argument
|
|
95
|
+
per line, from the subset the harness understands: `--profile NAME`,
|
|
96
|
+
`--macros FILE`, `rename --from X --to Y`). The harness runs every case,
|
|
97
|
+
compares bytes, then asserts idempotence: `expected.tex` through the same
|
|
98
|
+
profile is unchanged. Cases tagged for round
|
|
99
|
+
trips are run unicode to ascii to unicode and must return to the start.
|
|
100
|
+
|
|
101
|
+
Every case is also compiled before and after (`tests/verify.rs` runs
|
|
102
|
+
`mathfmt --verify` on `input.tex` with the case's `args`) and must render the
|
|
103
|
+
same pages. A case whose conversion changes the typeset result on purpose (a
|
|
104
|
+
rename; a spelling that repairs pdflatex) says so in `expected.verify`:
|
|
105
|
+
`differs: reason`. One that cannot compile at all says `skip: reason`. A
|
|
106
|
+
snippet must be compilable: define the macros it uses, and keep preamble-only
|
|
107
|
+
commands (`\usepackage`, `\DeclareMathOperator`) on their own lines, which the
|
|
108
|
+
wrapper hoists.
|
|
109
|
+
|
|
110
|
+
- Test first: the fixture or unit test that pins the behaviour, watched to fail,
|
|
111
|
+
then the code.
|
|
112
|
+
- Every bug becomes a fixture named for what it pins.
|
|
113
|
+
- Never weaken a test or hand-edit an `expected.tex` to make a run pass. An
|
|
114
|
+
expected file is read, not copied from the output.
|
|
115
|
+
|
|
116
|
+
## Tables
|
|
117
|
+
|
|
118
|
+
- A symbol, an alias or a preferred ASCII spelling is a row in `latex.tsv`; a
|
|
119
|
+
command's argument count or text-argument flag is a row in `signatures.tsv`.
|
|
120
|
+
Not a match arm.
|
|
121
|
+
- `unicode-math.tsv` and `alphabets.tsv` are regenerated only by
|
|
122
|
+
`tools/gen-table.py`, when TeX Live updates; review the diff before adding it.
|
|
123
|
+
- `mathfmt table` prints what the binary believes; trust it over memory.
|
|
124
|
+
|
|
125
|
+
## Rules
|
|
126
|
+
|
|
127
|
+
These are the person's standing rules. They bind every seat and every session.
|
|
128
|
+
|
|
129
|
+
- **Commits.** `area: what changed`, lowercase, as `git log` shows, a title of
|
|
130
|
+
60 characters or fewer and a body that says why. Never put a model identifier
|
|
131
|
+
(opus, sonnet, haiku, fable, mythos or any other) in a commit's title or
|
|
132
|
+
body. The harness's own `Co-Authored-By` trailer is the exception. Never skip
|
|
133
|
+
hooks and never change git identity.
|
|
134
|
+
- **Leave alone what is not yours.** Don't touch another session's uncommitted
|
|
135
|
+
work, and don't stop processes you did not start.
|
|
136
|
+
- **Refusals.** When the permission system refuses a step, report it. Never
|
|
137
|
+
route around it, and never ask another seat to do it for you.
|
|
138
|
+
- **Releases.** The person lands, pushes, tags and releases. Nothing reaches
|
|
139
|
+
`main` any other way.
|
|
140
|
+
- **Review as you go.** Every wave gets an independent adversarial review
|
|
141
|
+
before it lands; each finding is fixed or ruled on before the next wave.
|
|
142
|
+
- **The spec matches what ships.** When behaviour departs from the design,
|
|
143
|
+
amend the spec in the same change.
|