Maintaining the library
AI assistant
Build this with Claude Code, Cursor or Copilot
Copy a Tessero-tuned prompt for Maintaining the library, grounded in 16 real API signatures , into your IDE's AI. No chatbot, just exact context.
This is the maintainer runbook for changing the library itself - building the
kernel, regenerating fixtures, running the gate, deploying to the reference host,
and publishing issues. It is distinct from the rest of the operations manual,
which is for users running Tessero in their own applications (see
[Deployment(https://github.com/DMJ-CV-91913/tessero/blob/main/docs/operations/deployment.md) and [Upgrading(https://github.com/DMJ-CV-91913/tessero/blob/main/docs/operations/upgrading.md)), and from the
day-to-day numerical-parity rules in prompts/numpy-scipy-parity-program.md,
CONTRIBUTING
and RELEASING.
The two machines
Development is split across two hosts on purpose:
| Role | Host | Has | Notes |
|---|---|---|---|
| Dev checkout | your workstation | the repo, GitHub push credentials | edits happen here; commits originate here |
| Build / reference host | <server> (SSH alias) |
C/C++ toolchain, the pinned .venv-parity, LAPACK |
all kernel builds, fixture generation, and gate runs happen here |
The workstation has no C toolchain, so the loop is: edit locally → sync to the reference host → build and verify there → commit and push from the workstation → deploy (pull) on the host. Commits only ever originate on the workstation because the reference host's git deploy key is read-only (it can pull, never push - see git access).
One-command update
On the reference host, a single script does the whole sync-build-test cycle:
bash tools/update.sh # force-sync to origin/main, build kernel+ext, C tests + FFI parity bash tools/update.sh --publish # ... and publish the beta issue list to GitHub (needs gh auth, once) bash tools/update.sh --quick # skip the ~4 min FFI parity run
update.sh force-syncs (git reset --hard origin/main), so it discards local
changes - it is for a mirror checkout, not a dirty working tree. It wraps
tools/pull-build.sh, which can also be run directly.
Deploy: pull-build.sh
bash tools/pull-build.sh # git pull --ff-only, build kernel + extension, C tests, FFI parity bash tools/pull-build.sh --quick # C tests only (skip FFI parity) bash tools/pull-build.sh --no-ext # FFI kernel only (skip the extension build)
The extension is built in a scratch copy ($EXT_BUILD, default ~/ext-build),
never in-place, so it can't pollute the tracked ext/ tree. The extension's
parallel build has an intermittent dependency-file race; pull-build.sh falls
back to a serial make automatically, so a flake self-recovers.
A clean deploy of a pushed batch, from any state:
cd ~/tessero git fetch origin git checkout -f main # -f discards any scp'd work-in-progress git reset --hard origin/main bash tools/pull-build.sh
Avoid git clean -fd on the reference host - it would remove .venv-parity and
vendor/, which are costly to rebuild and not needed to deploy.
The gate
tools/parity/gate.sh is the full acceptance gate (prompts/numpy-scipy-parity-program.md
section 8): generated-file checks, kernel build + tests + sanitizers, extension
build + tests, NumPy/SciPy fixtures on both backends, PHPUnit suites, the
runnable cookbook, fuzz + soak + benchmarks, and mkdocs build --strict.
source .venv-parity/bin/activate EXT_DIR=~/ext-build bash tools/parity/gate.sh # everything QUICK=1 EXT_DIR=~/ext-build bash tools/parity/gate.sh # skip sanitizers, fuzz, soak, benchmarks
Use QUICK=1 on an AVX2 host. The benchmark thresholds are calibrated for an
AVX-512 reference host, so the rng normal bench gate reports a spurious failure
on AVX2 (tracked as TSR-206); the stress block (fuzz/soak/bench) is pure
C-kernel and orthogonal to most changes. QUICK=1 runs the full functional
verification and yields a clean GATE_EXIT=0 on AVX2.
Fixtures and the pinned environment
Reference values are generated by SciPy/NumPy in a pinned environment (ADR 0012) so they do not depend on the host's SIMD dispatch:
source .venv-parity/bin/activate bash tools/parity/gen-fixtures.sh # every group bash tools/parity/gen-fixtures.sh legacy np # some groups
gen-fixtures.sh sets NPY_DISABLE_CPU_FEATURES (so NumPy's float64 loops are
the C library's on every CPU) and OPENBLAS_CORETYPE + single-threaded BLAS (so
reductions through BLAS use one summation order). The generators refuse to write
without the pinned environment (TSR_FIXTURE_UNPINNED=1 for a throwaway file).
Host constraint (TSR-207). The canonical fixture host is AVX2 (Haswell).
The pin was re-baselined from SkylakeX (AVX-512) to Haswell because the
SkylakeX OpenBLAS kernel is an illegal instruction on an AVX2 CPU - any NumPy
call segfaults. Consequences:
- Generate fixtures on an AVX2+ host. Single-threaded BLAS keeps the summation order deterministic; Haswell kernels run on any AVX2+ CPU.
- Transcendental fixtures can differ by ~1 ulp from another host's glibc/libm, within the test tolerances - expected, not a regression.
fixtures.py(scipy.special) is CPU-independent and slow (hours) on this host; don't regenerate the special/stats groups unless they actually changed. The committed fixtures already pass here (the gate'srun-tests.phpis 777/0).
Adding a function
New numerical functions go into the kernel function registry (ADR 0011), so both backends get them from one implementation. The per-item loop:
- Spec/table entry -
spec/*.yaml(element-wise), orcsrc/src/np_*.c/csrc/cxx/*.cpp(routines), or atools/parity/scope.yamlrow. - Fixtures -
bash tools/parity/gen-fixtures.sh <group>(pinned env). - Implementation + build -
make -C csrc;bash tools/sync-ext.shto mirror the kernel intoext/libtessero. - Façades - regenerated by
tools/parity/gen-facades.phpafter a build. - Verify both backends -
php tools/parity/run-tests.php --backend=ffiand--backend=ext. - Ledger - update
.work/ledger.md.
A hand-written loop ufunc (e.g. heaviside) is a LOOP2 + a table row in
csrc/src/ufunc.c, a wrapper on Tessero\Math, a fixture in
tests/fixtures/generate_ufunc.py, and the ufunc-count assertions in
csrc/tests/test_main.c, tests/Unit/MathTest.php and tests/docs/examples.php.
Coverage and metrics (regenerate, never hand-edit)
The coverage page is generated; regenerate it whenever the public API changes:
php -d ffi.enable=1 -d extension=<tessero.so> tools/api-dump.php > tools/parity/api.json python3 tools/gap_inventory.py tools/parity/api.json > docs/project/numpy-scipy-coverage.md python3 tools/parity/report.py # metrics.md, metrics.json, deviations.md, usage.md
Two rules the generators do not enforce on their own (TSR-121):
tools/api-dump.phpmust list every generated façade class, or that class's area reads 0 %.tools/gap_inventory.pymust reference every shipped function. The generator only errors on a reference to a function that does not exist - it never flags a shipped function with no reference, so the page silently understates coverage if a row is left empty. After adding functions, wire their rows.
Publishing issues
The beta issue list is one source with two outputs (tools/beta-issues.php):
php tools/beta-issues.php render > docs/project/open-issues.md # the snapshot page (gate checks it is current) php tools/beta-issues.php check # CI/gate: fail if the page is stale gh auth login # once, interactive php tools/beta-issues.php create --repo=DMJ-CV-91913/tessero # create one GitHub issue per entry (idempotent)
create uses the GitHub CLI and needs it authenticated once. It is
idempotent (skips IDs already present as issues). Edit issues in
.github/beta-issues.json, then render to refresh the page.
The website then pulls the issues onto its Contribute → Issues page with
php artisan issues:sync (run in the web app, not here). Because the repo is
private, the web app needs a GITHUB_TOKEN with issues read access in its .env;
the token is server-side only and never reaches visitors.
Git access on the reference host
The reference host authenticates to GitHub with a read-only deploy key
(~/.ssh/github_deploy). Pin it in ~/.ssh/config so git doesn't offer the
wrong key (a scopeless or website-scoped key yields Permission denied):
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_deploy
IdentitiesOnly yes
The key can fetch/pull but never push - all pushes originate from the dev
workstation. A reboot that clears a loaded ssh-agent can re-expose a wrong-key
problem; the ~/.ssh/config entry above makes the key selection explicit.