GRIT is a specification with five independent implementations that must agree bit-for-bit. That shapes everything below: the interesting work is not "make my implementation pass", it is "keep all six artifacts (the spec, the reference, and the five implementations) saying exactly the same thing".
Please read the two rules in The conformance rule and
Zero normative drift before opening a pull request that
touches anything under spec/.
| Path | What it is | Normative? |
|---|---|---|
spec/SPEC.md |
The normative text | Yes |
spec/reference.py |
Executable reference; the only generator of the vector suite | Yes |
spec/conformance/vectors.json |
Shared vector suite (68 entries), generated | Yes (derived) |
c/, cpp/, rust/, python/, typescript/ |
Conformant implementations | No — they follow |
design/, audit/, CONFORMANCE_REPORT.md |
Non-normative record | No |
Every suite reads the same spec/conformance/vectors.json. Run the reference
first: if it fails, nothing downstream is meaningful.
cd spec
python3 reference.py selftest # build the suite in memory + verify it -> "selftest: 0 failures"
python3 reference.py verify # verify the checked-in file -> "68 entries, 0 failures"
python3 reference.py grades # print the reference grades and their gidscd c
make clean && make test
# -> checks passed: 910 failed: 0Built with -std=c11 -Wall -Wextra -Werror -ffp-contract=off -fno-fast-math.
The FP flags are not optional: SPEC §1.1 forbids substituting an FMA for a
specified multiply-then-add, and GCC contracts by default even in ISO mode.
cd cpp
make clean && make test # -std=c++20, header-only
make strict # + -Wpedantic -Wshadow -Wconversion
make asan # ASan + UBSan; backs the §14 "no UB" claim
# -> === 801 passed, 0 failed ===cd rust
cargo test --release # 27 unit + 9 conformance + 1 doc-testcd python
python3 -m unittest discover # -> Ran 124 tests ... OKNo third-party dependencies. numpy is optional and must never be imported at
module scope — a run in a numpy-free environment is part of the contract.
cd typescript
npm ci # or: npm install
npm test # runs `tsc -p tsconfig.json` then `node --test dist/test/*.test.js`CI (.github/workflows/ci.yml) runs all six of the above on ubuntu-latest
with plain toolchains (gcc, g++, rustup stable, python3, node 20), plus a final
job that pins the SHA-256 of the vector suite. Reproduce CI locally by running
each block above in order.
spec/conformance/vectors.json is generated only by spec/reference.py, and
implementations must match it bit-exactly.
Concretely:
-
Never hand-edit
vectors.json. Not one byte, not a comment, not the indentation. The only legal way to change it is:cd spec $EDITOR reference.py # change the reference python3 reference.py gen # regenerate the whole file python3 reference.py verify # re-verify the file you just wrote
A PR whose diff touches
vectors.jsonwithout a correspondingreference.pychange will be rejected on sight. -
The vectors are the arbiter, not your implementation. If an implementation disagrees with a vector, the implementation is wrong until the spec says otherwise. Fix the implementation, or open a spec issue first and win the argument there. Do not "adjust" a vector to make a build green.
-
Bit-exact means bit-exact. Encoded plane bytes, the 64-byte descriptor image,
gidvalues, canonical grade strings, and error codes on the negative vectors must all match exactly. "Numerically close" is a failure. Decoded values are compared as bit patterns, including signed zero and NaN payloads where the spec fixes them. -
All five implementations must stay green in the same commit. If a reference change makes four languages pass and one fail, the PR is not ready. Split it if you must, but the merge commit must be green everywhere.
-
Vector-suite SHA-256 is pinned in CI. The current pin is
9d6fac62f89f528b38036add718fb4ba316a10803e3551cf908d70d37b71aee9A PR that legitimately regenerates the suite must update that pin in
.github/workflows/ci.ymlin the same commit, and say so in the PR description. An unexplained hash change is the signature of exactly the accident this rule exists to catch.
A pull request may not silently change observable behaviour.
The following are normative surfaces. Touching any of them is a spec change, not an implementation change:
- encoding and decoding rules, including rounding, saturation, and the handling of NaN/Inf/subnormals;
- the 64-byte descriptor layout — field order, offsets, widths, endianness, reserved bytes, and the rule that reserved bytes MUST be zero;
- canonicalisation and hashing: the canonical grade string, the FNV-1a
construction,
gid, and any other fingerprint; - grades, including the reference grades in §10 and the meaning of any field in the grade vocabulary;
- placement values, the placement id, and the audit/witness discipline;
- error codes and which inputs produce them;
- anything any implementation currently asserts in a test.
Rules for spec PRs:
- Say so in the title. Prefix with
spec:. A PR that changesspec/and is not labelled as a spec change will be treated as an accident. - The spec text and the reference change together.
SPEC.mdandreference.pyare two views of one thing; a PR that updates only one of them is incomplete. - Regenerate, then propagate.
python3 reference.py gen, then update all five implementations, in the same PR, with all suites green. - State the compatibility impact explicitly. In the PR body, answer: does
any previously valid descriptor become invalid? Does any
gidchange? Does any decode change value? If the answer to any of these is yes, the change needs a version bump and a supersedes entry, not a patch release. - Non-normative edits must prove they are non-normative. Typo fixes, clarifications, and examples are welcome — but if the vector-suite SHA-256 changes, the edit was not non-normative. That is the test, and it is mechanical.
Refactors, performance work, new platform support, better error messages, and additional implementation-side tests are all welcome and need none of the above — as long as the vector suite still passes and its hash is unchanged.
A sixth language is welcome. The bar:
- Zero runtime dependencies. All five existing implementations are pure stdlib. This is a hard requirement, not a preference: GRIT must be usable in a freestanding or vendored context.
- Runs the shared suite. Your harness must consume
spec/conformance/vectors.jsondirectly — no transcribed constants, no language-local copy of the vectors. - Covers all four sections. Positive (34), negative (15), fingerprint (16), and supersedes (3). Negative vectors must produce the exact specified error code, and no input — including adversarial 64-byte descriptors — may trap, overflow, or invoke undefined behaviour.
- Has a one-line test command that exits non-zero on failure, and a CI job alongside the others.
-
cd spec && python3 reference.py verifypasses. -
sha256sum spec/conformance/vectors.jsonis unchanged, or the change is intentional, generated byreference.py gen, explained in the PR body, and the CI pin is updated in the same commit. - All five implementation suites pass locally.
- No new runtime dependency in any implementation.
- If
spec/is touched: title prefixedspec:,SPEC.mdandreference.pyupdated together, compatibility impact stated. - Build outputs are not committed. Note that some build artifacts predate
.gitignoreand are still tracked; if you are cleaning those up, do it in a dedicated PR (git rm -r --cached <path>) with no other changes.
- A disagreement between implementations is the highest-severity bug class
here. Include the descriptor bytes (hex), the
gid, the plane bytes, and the differing outputs per language. - Ambiguity in
SPEC.mdis a real bug even when every implementation passes: if two readers can implement it differently, the text is wrong. Quote the section and give the two readings. - Security-relevant findings (any input to
grit_checkthat traps, reads out of bounds, or loops unboundedly) should be reported privately to the maintainers before public disclosure.
By contributing, you agree that your contributions are licensed under the
Apache License 2.0 (see LICENSE).