an.timing.contract#

The timing kernel’s cross-language contract: five JSON files, generated from the registries.

The contract lives in an/data/timing/ (shipped in the wheel) and is what any other implementation of the kernel — the TypeScript one, a package that copies an’s track format — copies and asserts:

easing.json

every registered easing (name, version, family, solver) with sample values, plus parametrised examples

kinds.json

every field kind with sample interpolations, and the seeded property spaces

timeline.schema.json

the authored flat timeline (start, end, address, change)

compiled.schema.json

tracks, placed clips, loop modes, channels — the compiled form runtime.js consumes

timing_vectors.json

compiled documents and the state at listed times

Numbers agree within 1e-9 * max(1, |x|, |y|); strings, booleans, null, list lengths and object keys agree exactly. A state is SPARSE: an address no clip has started writing is absent (at rest).

The files are regenerated by python -m an.timing.contract write and checked by python -m an.timing.contract check (a test runs the check). Regenerating is a deliberate act: a change to a committed sample is a change of the contract.

>>> doc = {"timeline": {"duration": 1.0, "tracks": [{"clips": [
...     {"animation_id": "m", "start_time": 0.0}]}]},
...     "animations": {"m": {"duration": 1.0, "channels": [{"target": "cam",
...     "property": "yaw", "keyframes": [{"time": 0.0, "value": 350.0},
...     {"time": 1.0, "value": 10.0}]}]}}}
>>> space = {"fields": [{"pattern": "yaw", "spec": {"kind": "angle"}}]}
>>> state_at(doc, 0.5, space=space)
{'cam:yaw': 360.0}

Module Attributes

CONTRACT_DIR

Where the committed contract files live (inside the package, so they ship).

REL_TOL

|x - y| <= REL_TOL * max(1, |x|, |y|).

VALUE_TYPED_CASE_SPACE

The cases whose space is the value-typed rule's write-group space are ALSO evaluated with space=VALUE_TYPED — the rule runtime.js implements — so both rules are held to the same numbers (the default, space=None, is the declared rule since an#239 item 2).

SOLVER_PROBE_U

a uniform grid, plus points that reach the solvers' fallbacks (near the ends, and around the middle of a curve whose slope vanishes there, e.g. cubic-bezier(1, 0, 0, 1)).

PARAMETRIC_EXAMPLES

Parametrised specs the easing file samples, beside the named entries.

CSS_IMPORT_ALIASES

How a CSS-named document's names map onto this registry where the name means something else here.

ADDRESS_PATTERN

The address grammar as a regular expression (the parser is an.timing.address).

Functions

build_compiled_schema()

build_contract()

Every contract file's content, by file name, generated from the registries.

build_easing()

build_kinds()

build_timeline_schema()

build_vectors()

check_vectors([vectors])

Every sample of vectors (default: the committed file) that an.timing does not reproduce, as one line each.

contract_drift([directory])

How the committed files differ from what the registries generate today.

dumps(obj)

The committed text of a contract file (stable, diffable).

load_contract_file(name, *[, directory])

main([argv])

python -m an.timing.contract [write|check].

resolve_space(space)

A vector case's space: a registered space's name, or its JSON form.

state_at(doc, t, *, space)

The sparse state of compiled document doc at t, keyed by address.

values_close(expected, actual, *[, rel_tol])

The contract's comparison (see the module docstring).

write_contract([directory])

Regenerate every contract file under directory.

an.timing.contract.ADDRESS_PATTERN: str = '^[^:/@\\s](?:[^:/@]*[^:/@\\s])?(?:/[^:/@\\s](?:[^:/@]*[^:/@\\s])?)*:[^:/@.\\s](?:[^:/@.]*[^:/@.\\s])?(?:\\.[^:/@.\\s](?:[^:/@.]*[^:/@.\\s])?)*(?:@[^:/@\\s](?:[^:/@]*[^:/@\\s])?)?$'#

The address grammar as a regular expression (the parser is an.timing.address).

an.timing.contract.CONTRACT_DIR: Path = PosixPath('/home/runner/work/an/an/an/data/timing')#

Where the committed contract files live (inside the package, so they ship).

an.timing.contract.CSS_IMPORT_ALIASES: dict[str, str] = {'ease': 'cubic-bezier(0.25, 0.1, 0.25, 1)'}#

How a CSS-named document’s names map onto this registry where the name means something else here. ease is an’s quadratic (ADR 0001 decision 10, amended 2026-10-01); a CSS document’s ease is this spec.

an.timing.contract.PARAMETRIC_EXAMPLES: tuple[Any, ...] = ('cubic-bezier(0.25, 0.1, 0.25, 1)', 'cubic-bezier(0.5, -0.8, 0.5, 1.8)', 'cubic-bezier(0.3, 1.8, 0.7, -0.8)', 'steps(4)', 'steps(4, jump-start)', 'steps(3, jump-none)', 'steps(2, jump-both)', 'steps(2, start)', 'cubic-bezier(1, 0, 0, 1)', [0.42, 0.0, 0.58, 1.0], [0.5, 2.0, 0.5, 2.0], [0.3, 3.0, 0.7, 0.0], [1.0, 0.0, 0.0, 1.0], [0.42, 0.0, 1.0, 1.0], [0.0, 0.0, 0.58, 1.0])#

Parametrised specs the easing file samples, beside the named entries.

an.timing.contract.REL_TOL: float = 1e-09#

|x - y| <= REL_TOL * max(1, |x|, |y|).

Type:

The contract’s tolerance

an.timing.contract.SOLVER_PROBE_U: tuple[float, ...] = (1e-06, 0.4, 0.501, 0.999999)#

a uniform grid, plus points that reach the solvers’ fallbacks (near the ends, and around the middle of a curve whose slope vanishes there, e.g. cubic-bezier(1, 0, 0, 1)).

Type:

Where every easing is sampled

an.timing.contract.VALUE_TYPED_CASE_SPACE: str = 'stage.node'#

The cases whose space is the value-typed rule’s write-group space are ALSO evaluated with space=VALUE_TYPED — the rule runtime.js implements — so both rules are held to the same numbers (the default, space=None, is the declared rule since an#239 item 2).

an.timing.contract.build_contract()[source]#

Every contract file’s content, by file name, generated from the registries.

Return type:

dict[str, Any]

an.timing.contract.check_vectors(vectors=None)[source]#

Every sample of vectors (default: the committed file) that an.timing does not reproduce, as one line each. Empty means the kernel meets the contract.

Both evaluation rules are held: every case under its declared space, and every stage.node case also under the value-typed rule (space=VALUE_TYPED).

Return type:

list[str]

an.timing.contract.contract_drift(directory=PosixPath('/home/runner/work/an/an/an/data/timing'))[source]#

How the committed files differ from what the registries generate today.

Numbers compare with the contract’s tolerance, because a transcendental function (exp, sin, pow) may differ in its last bit across platforms’ maths libraries; anything else must match exactly.

Return type:

list[str]

an.timing.contract.dumps(obj)[source]#

The committed text of a contract file (stable, diffable).

Return type:

str

an.timing.contract.main(argv=None)[source]#

python -m an.timing.contract [write|check].

Return type:

int

an.timing.contract.resolve_space(space)[source]#

A vector case’s space: a registered space’s name, or its JSON form.

Return type:

PropertySpace

an.timing.contract.state_at(doc, t, *, space)[source]#

The sparse state of compiled document doc at t, keyed by address.

Return type:

dict[str, Any]

an.timing.contract.values_close(expected, actual, *, rel_tol=1e-09)[source]#

The contract’s comparison (see the module docstring).

Return type:

bool

>>> values_close(1e9, 1e9 + 0.5), values_close([1, "a"], [1.0, "a"]), values_close(True, 1)
(True, True, False)
an.timing.contract.write_contract(directory=PosixPath('/home/runner/work/an/an/an/data/timing'))[source]#

Regenerate every contract file under directory.

Return type:

list[Path]