effx
Concepts

Semantic hash

Canonical JSON per RFC 8785 over the normalized IR, and the sha-256 hash taken from it. Same meaning gives the same bytes.

The same application graph must give the same cache key, diff, snapshot and agent input. effx gets that from three steps: normalize, canonicalize, hash (ADR 0003).

Source: packages/ir/src/{normalize,canonical,jcs}.ts, docs/decisions/0003-canonical-json-is-a-projection.md, docs/specs/0001-compiler-kernel.md.

IR ──normalize──▶ IR₀ ──encode (Schema)──▶ JSON ──JCS──▶ canonical text ──sha-256──▶ semanticHash
                                                          (ir.json)                  (manifest.json)

The steps

StepRuleFunction
normalizenodes sorted by id, edges by (kind, from, to, qualifier), set-valued fields sorted and deduplicated. Idempotent.normalize
canonicalRFC 8785 over the Schema-encoded JSON of the normalized IRcanonical, canonicalJson
semanticHashlowercase hex sha-256 of the canonical UTF-8 bytessemanticHash

Canonical JSON

canonicalJson implements RFC 8785 (JCS):

RuleDetail
Object keyssorted by UTF-16 code unit
Whitespacenone
PrimitivesES6 JSON.stringify formatting for strings, numbers, booleans and null
Arraysorder is kept. Order is meaningful in JSON, so normalization fixes it first.

Encoding is derived from the IR Schema (Schema.toCodecJson), so no hand-written serializer defines the wire shape.

Hash

semanticHash(ir) is an Effect that needs the Crypto service. It takes the SHA-256 digest of the UTF-8 bytes of canonical(ir) and hex-encodes it. The CLI provides Crypto from the Bun platform layer.

effx build writes the canonical text to .effx/ir.json and the hash to semanticHash in .effx/manifest.json (effx build).

What changes the hash, and what does not

ChangeHash
Moving a declaration between lines or files without changing its idsunchanged. Locations are in the manifest, not the IR.
Reordering declarations in a fileunchanged. Normalization sorts.
Changing emit mode (contract, handlers, all)unchanged
Changing target profile (effect-4.0)unchanged
Changing the output directory, the absolute checkout path, or a .js versus .ts import suffixunchanged
Changing a declaration (an operation, a path, a schema reference, a declared error)changed

Spec 0010 states the output-policy rows as a requirement: two passes of one source with different emit modes, tsconfigs and output directories must produce byte-identical canonical IR and the same semanticHash. See Emit modes and targets.

Laws

These are tested properties, listed in ADR 0003 and spec 0001.

LawMeaning
decode(encode(normalize(x))) == normalize(x)round trip
canonical(canonical(x)) == canonical(x)canonicalization is idempotent
normalize(normalize(x)) == normalize(x)normalization is idempotent
fromGraph(toGraph(x)) == normalize(x)the graph index loses nothing

Two files, two jobs

FileHolds
.effx/ir.jsonsemantics only
.effx/manifest.jsonlocations, compiler versions, generated files, diagnostics and the hash

On this page