Repository navigation
Architecture
fiscalclock is one Rust core with thin Python and npm bindings that return the core's JSON unchanged, and this page describes that structure as built in 0.1.0.
Last reviewed: 1 Nov 2026
The decision behind this structure is ADR-0002. The CLI and MCP server are planned for 0.4 and are not built (see Agents-and-MCP).
flowchart TB
def["JSON definition<br/>(supplied by the user)"]
subgraph core["crates/core: Rust crate fiscalclock"]
parse["Parse and validate<br/>(five blocks)"]
arith["Calendar arithmetic<br/>(own Date type)"]
api["JSON API<br/>fiscalclock::json::validate / call"]
parse --> arith --> api
end
def --> parse
api --> rust["Rust crate<br/>crates.io: fiscalclock"]
api --> py["PyO3 extension _fiscalclock<br/>+ Python layer<br/>PyPI: fiscalclock"]
api --> wasm["wasm-bindgen module<br/>+ TypeScript layer<br/>npm: fiscalclock"]
vectors[("conformance/vectors<br/>112 files, 4,070 cases")]
vectors -.-> rust
vectors -.-> py
vectors -.-> wasm
rust -.-> t1["rust.txt transcript"]
py -.-> t2["python.txt transcripts"]
wasm -.-> t3["js.txt transcripts"]
t1 & t2 & t3 -.-> cmp{{"CI: identical-output<br/>byte-for-byte comparison"}}
Solid arrows are calls. Dotted arrows are the test path: every package runs the same vectors and writes a transcript of the raw JSON results, and CI compares the transcripts.
All three packages were released at version 0.1.0 on 31 Oct 2026 (v0.1.0); see the CHANGELOG.
| Package | Built with | Accepts dates as | Distribution | Source |
|---|---|---|---|---|
Rust crate fiscalclock
|
Plain Rust, #![no_std] with alloc, #![forbid(unsafe_code)]; dependencies serde, serde_json, sha2; minimum Rust 1.85 |
The crate's own Date; ISO strings through the JSON API |
crates.io | crates/core |
PyPI fiscalclock
|
PyO3 extension _fiscalclock built with maturin, plus a pure-Python layer with type stubs |
An exact datetime.date or an ISO string |
One abi3 wheel for CPython 3.10 and later, and one wheel for free-threaded CPython 3.14 (cp314t), on each of 7 platforms: manylinux x86_64 and aarch64, musllinux x86_64 and aarch64, macOS x86_64 and arm64, Windows x64. Plus an sdist. |
bindings/python |
npm fiscalclock
|
Core compiled to WebAssembly with wasm-bindgen directly (no wasm-pack), plus a TypeScript layer | An ISO string or a Temporal.PlainDate
|
ES module with TypeScript types. The node export loads the WebAssembly from disk; the default export, for browsers and other environments, fetches it relative to its own URL with top-level await. Node.js 22 or later. |
bindings/js |
Sources for the table: the wheel matrix in release.yml, the exports in package.json, and the package READMEs. The wheel choices follow ADR-0011: PEP 803 states that "The Stable ABI is currently not available for free-threaded builds", and its abi3t variant targets Python 3.15 (status Final as of 31 Oct 2026). The npm toolchain is ADR-0020. The minimum versions are ADR-0025, and the msrv job in ci.yml builds the core with Rust 1.85.
The core has its own date type and no date-crate dependency (ADR-0003). The proposal's optional chrono, jiff and time conversions are planned for 0.2.
Every operation is defined once in the core as JSON text in and JSON text out: fiscalclock::json::validate and fiscalclock::json::call (spec/api-v1.md). The operations in 0.1.0 are validate, locate, period, shift, parent, children, years, format, parse, dimension, dimension_csv and explain.
The bindings own only type conversion (AGENTS.md, design rule 9).
- They turn native values into JSON arguments, call the core, and turn the JSON result into native values (
datetime.datein Python; plain objects with ISO strings in JavaScript). - Their
call_json/callJsonandvalidate_json/validateJsonfunctions return the core's JSON text unchanged, with no reordered keys or reformatted values. - They reject timestamp types before calling the core, using an error the core builds, so the error is identical in both languages (see Semantics).
- They hold no calendar arithmetic and no validation logic.
Errors cross the boundary as data: {"errors":[{"code","path","message","hint","details"}]}. Python raises FiscalclockError (a ValueError) and JavaScript throws FiscalclockError, each carrying the same fields (ADR-0010).
Cross-language identity is checked by comparing JSON output, not native objects (ADR-0019).
- The Rust, Python and JavaScript conformance runners each run every case in
conformance/vectors/through the raw JSON API. - Each writes a transcript, one line of raw output per case, to
target/transcripts/rust.txt,python.txtorjs.txt. - The
identical-outputjob inci.ymldownloads 14 transcripts and compares each with the Rust transcript usingcmp: one Rust; seven Python (CPython 3.10 and 3.14 on Ubuntu, Windows and macOS, plus free-threaded 3.14 on Ubuntu); six JavaScript (Node.js 22 and 26 on the same three systems). Any differing byte fails the job.
In CI run 41222104138 on 31 Oct 2026 this job passed.
release.yml runs on a tag vX.Y.Z, or manually as a dry run that stops before attestation and publishing.
flowchart LR
tag["Tag vX.Y.Z"] --> check["check<br/>versions agree, tag on main,<br/>changelog section exists"]
check --> build["build<br/>crate, 14 wheels, sdist,<br/>npm tarball"]
build --> smoke["smoke<br/>install and run on<br/>Linux, macOS, Windows"]
smoke --> attest["attest<br/>build provenance"]
attest --> pubc["publish crates.io<br/>(approval)"]
attest --> pubp["publish PyPI<br/>(approval)"]
attest --> pubn["publish npm<br/>(approval)"]
pubc & pubp & pubn --> rel["GitHub release<br/>from changelog"]
-
check: every manifest states the same version; on a tag, the commit is on
mainandCHANGELOG.mdhas a section for the version. -
build:
cargo packagefor the crate; maturin for the wheels and sdist;npm packfor the npm tarball after its tests pass. -
smoke: on Ubuntu, macOS and Windows, the built wheel is installed on CPython 3.10 and the packed tarball on Node.js 22, and
scripts/smoke.pyandscripts/smoke.mjscheck a known NRF result and the timestamp rejection. -
attest:
actions/attest-build-provenancesigns provenance for every artefact (tag runs only). -
publish: three jobs, one per registry, each in a GitHub environment (
crates-io,pypi,npm) that needs the maintainer's approval. PyPI uses trusted publishing; crates.io and npm use tokens for the first release (see Risks). - GitHub release: created from the changelog section once all three publishes succeed.
Every action is pinned to a full commit SHA, and each job gets only the permissions it needs. The dry run 41222111858 on 31 Oct 2026 built every artefact and passed the smoke tests; the publish jobs were skipped, as designed for a dry run.
| Part | Holds | Never holds |
|---|---|---|
crates/core |
Parsing, validation, all arithmetic, labels, the JSON API | I/O, a clock, global state, named calendars |
bindings/python, bindings/js
|
Type conversion and the JSON pass-through | Calendar arithmetic or validation |
cli (planned for 0.4) |
The command-line interface and MCP server | Logic not reachable through the core's JSON API |
The full map is in AGENTS.md.
fiscalclock is licensed under MIT OR Apache-2.0 (MIT, Apache-2.0). Last reviewed: 31 Oct 2026. Found an error on this page? Report it with the documentation form.