Skip to content

Latest commit

 

History

History
237 lines (180 loc) · 9.1 KB

File metadata and controls

237 lines (180 loc) · 9.1 KB

Contributing to GRIT

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/.


Repository map

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

Running the suites

Every suite reads the same spec/conformance/vectors.json. Run the reference first: if it fails, nothing downstream is meaningful.

Reference (Python 3, stdlib only)

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 gids

C — 910 checks

cd c
make clean && make test
# -> checks passed: 910   failed: 0

Built 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.

C++ — 801 checks

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 ===

Rust — 37 tests

cd rust
cargo test --release        # 27 unit + 9 conformance + 1 doc-test

Python — 124 tests

cd python
python3 -m unittest discover   # -> Ran 124 tests ... OK

No 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.

TypeScript — 91 tests

cd typescript
npm ci        # or: npm install
npm test      # runs `tsc -p tsconfig.json` then `node --test dist/test/*.test.js`

Everything at once

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.


The conformance rule

spec/conformance/vectors.json is generated only by spec/reference.py, and implementations must match it bit-exactly.

Concretely:

  1. 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.json without a corresponding reference.py change will be rejected on sight.

  2. 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.

  3. Bit-exact means bit-exact. Encoded plane bytes, the 64-byte descriptor image, gid values, 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.

  4. 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.

  5. Vector-suite SHA-256 is pinned in CI. The current pin is

    9d6fac62f89f528b38036add718fb4ba316a10803e3551cf908d70d37b71aee9
    

    A PR that legitimately regenerates the suite must update that pin in .github/workflows/ci.yml in 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.


Zero normative drift

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:

  1. Say so in the title. Prefix with spec:. A PR that changes spec/ and is not labelled as a spec change will be treated as an accident.
  2. The spec text and the reference change together. SPEC.md and reference.py are two views of one thing; a PR that updates only one of them is incomplete.
  3. Regenerate, then propagate. python3 reference.py gen, then update all five implementations, in the same PR, with all suites green.
  4. State the compatibility impact explicitly. In the PR body, answer: does any previously valid descriptor become invalid? Does any gid change? 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.
  5. 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.


Adding a new implementation

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.json directly — 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.

Pull request checklist

  • cd spec && python3 reference.py verify passes.
  • sha256sum spec/conformance/vectors.json is unchanged, or the change is intentional, generated by reference.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 prefixed spec:, SPEC.md and reference.py updated together, compatibility impact stated.
  • Build outputs are not committed. Note that some build artifacts predate .gitignore and are still tracked; if you are cleaning those up, do it in a dedicated PR (git rm -r --cached <path>) with no other changes.

Reporting problems

  • 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.md is 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_check that traps, reads out of bounds, or loops unboundedly) should be reported privately to the maintainers before public disclosure.

License

By contributing, you agree that your contributions are licensed under the Apache License 2.0 (see LICENSE).