typedstandards on PyPI: a thin Python wrapper that runs @typedstandards/cli (vendored, pinned
exactly in package.json and package-lock.json) as a child process. Python >=3.11; hatchling
with a build hook (hatch_build.py) that vendors the CLI; uv for everything.
The system python3 may be older than 3.11: run Python only through uv. Under a Node version
manager a non-interactive shell may have no node on PATH; load it first
(eval "$(fnm env)" && fnm use 24, or your equivalent).
uv sync— creates.venvand installs the package editable. The build hook runsnpm ci --omit=dev --ignore-scriptsintosrc/typedstandards/_vendor/(git-ignored), so tests drive the same tree a wheel ships, never a global CLI ornpx. It re-runs whenpyproject.toml,package.json,package-lock.jsonorhatch_build.pychange; force it withuv sync --reinstall-package typedstandards.- Another Python:
uv run --python 3.11 pytest. Another Node: put it first onPATH, or setTYPEDSTANDARDS_NODE. - Every test runs offline: an autouse fixture in
tests/conftest.pymakes socket connect, bind andcreate_connectionraise.pin's tests usehttpx.MockTransport.marimoandnbformatare in the dev group only, so the helpers' tests drive the realmo.md,mo.Htmland nbformat schema; the package never imports either at module load.
The checks CI runs (.github/workflows/ci.yml):
test (py<3.11|3.12|3.14>, <ubuntu-24.04|macos-15>, node 24)andtest (py3.12, ubuntu-24.04, node 22)—uv sync --locked, thenuv run pytest; the gate is0 failed.lint—uv run ruff check .anduv run ruff format --check ..wheel (build, install, smoke)—uv build(the sdist, then the wheel from it), the wheel installed into a fresh environment, andscripts/smoke_wheel.pyrun there with a throwaway seed.
The wrapper and the CLI need Node 20.19 or later (the CLI's engines.node). A
@typedstandards/host-core site build needs Node 22 or later. The README states both.
It holds no key, reads no signing seed, and computes none of the format's hashes (content hash,
envelope hash, node id): the CLI reads TYPEDSTANDARDS_SIGNING_SEED_B64 from the environment it
inherits and does all of the format's work. Guard tests, which must keep failing on an offender:
tests/test_guards.pywithtests/guards.py: no module undersrc/typedstandardsnames the seed variable; no call passesenv=or changes the process environment; no module importshashlib(orhmac, or hashlib's underscore modules) except P2'spin.py, whose digest is a signed assertion; at run time, every child inherits the environment unchanged and the wrapper never reads the seed variable. Each scanner is also driven over a tree of offenders.
Test code may generate a random seed and set it with monkeypatch.setenv; package code never
touches one. A failing test never prints environment values.
Never cat/head/tail/dump .env*, auth.json, credentials*, *.pem, *.key, ~/.ssh,
~/.aws. Read only by key name (grep/jq a field, never a value) or a command the tool
exposes; never load-and-print a credentials file, even redacted.
Every phase report (PR body and anchor-issue comment) carries:
- the phase branch, head SHA and
git diff --numstat main...HEAD, with the blast zone stated; - each acceptance criterion's red (the failing assertion's output) and its green, pasted;
- the full suite output on every Python and Node the matrix names, the clean-checkout wheel build and its smoke check, and gitleaks over the outgoing range;
- fixture provenance: each fixture's source, commit and SHA-256 (
tests/fixtures/README.md), with the byte-equal assertions called out; - the model the phase ran on; everything flagged and not fixed.
The orchestrator re-verifies evidence before merging; numbers an implementer reports do not pass a gate on their own.
Bracket every phase merge: rollback/pre-produce-py-p<n> at the pre-merge anchor and
rollback/produce-py-p<n>-merged at the merge commit. The orchestrator pushes them, or hands them
to the owner as part of the merge script; implementation sessions never do.
A global pre-push guard (gitleaks plus a keyword list) scans the added lines of every outgoing
commit, so a fix on top does not clear an earlier commit: the flagged bytes must be absent from all
pushed history. Pushes go to the owner as one command, after gitleaks git --log-opts="main..HEAD"
over the outgoing range is clean. Never bypass the guard and never tune its patterns on your own
initiative. .gitleaks.toml allows only Ed25519 did:key identifiers, which are public keys.
gitleaks reads .gitleaks.toml from the directory it runs in, so a branch that adds or changes
that file is pushed from its own worktree (git -C .worktrees/<phase> push …) until main carries
the change.
- Neutral phrasing everywhere: no stakeholder, organisation or person is named. This repository is public and its history is permanent.
git commit -son every commit; theSigned-off-by:email must equal the author email exactly. Commits are signed (SSH).- Work lands by PR to
mainas merge commits; never push tomain. Merging is the orchestrator's call on evidence in a gated sprint, the owner's otherwise. - Publishing to PyPI is the owner's act, through
scripts/publish.shfrom a clean checkout ofmainat the release commit (its header has the details).DRY_RUN=1builds the sdist and the wheel, checks them, smoke-tests the wheel in a fresh environment, prints each file's SHA-256 and uploads nothing. The live run, underop runwith the PyPI token's secret reference, uploads exactly the files the dry run built and reads the release back from PyPI for about five minutes.READ_BACK=1runs only the read-back, for a run that stopped after uploading. - A CLI upgrade reaches users as a wrapper release that moves the pin:
package.json,package-lock.json(npm install --package-lock-only --ignore-scripts) andCLI_VERSIONtogether;tests/test_version.pyfails on any one left behind. CHANGELOG.mdis a factual per-version record; changes collect under## Unreleased.