Athar
Semantic diff for IFC files. Compares BIM models at the entity/property level — not line-by-line text.
Athar (Arabic: أثار) — a trace left behind.
How it works
For each IFC file, Athar builds a signature vector per product/spatial entity:
vh_geometry— sha256 Merkle hash over the entity's geometry-domain subgraph, excluding placement, so identical components share it across locations.vh_data— Merkle hash over the data-domain subgraph (properties, materials, typing). GlobalId and OwnerHistory never enter any hash.vh_topology— WL-style gossip hash over context and spatial neighborhoods.- World-space placement matrix (quantized), centroid, and AABB from the resolved
ObjectPlacementchain.
Old/new entities are then matched by tiered pool reduction: each tier only looks at still-unmatched entities and emits disjoint 1:1 pairs, so there is no candidate explosion and no ambiguous fan-out by construction.
| Tier | Evidence | Score |
|---|---|---|
guid |
GlobalId unique on both sides + same class | 1.0 (identical vector) / 0.9 |
geometry_hash |
same class + full signature-vector equality (interchangeable entities, zipped 1:1) | 0.8 |
Anything weaker is reported as added+deleted rather than guessed: a corpus
survey over real revision, discipline, and unrelated model pairs
(docs/corpus/2026-06-10-corpus-survey.md) showed that topology-only and
spatial-proximity fallback matching never fired on a genuine revision pair and
only manufactured cross-model matches, so those tiers were removed. Duplicated
GlobalIds are never trusted as identity — those entities fall through to the
vector tier. Matching is deterministic and entirely algorithmic, with no
tuning knobs.
The delta report classifies every entity as added, deleted, modified, or
unchanged, with per-aspect detail (geometry/data/topology/placement,
placement_delta_mm), change_scope (intrinsic = the entity itself changed,
transitive = only its neighborhood changed, mixed), and matcher
diagnostics (per-tier match counts, duplicate-GUID counts). Reports also carry
an audit block with producer/canon metadata plus old/new input provenance:
path, schema, byte size, and sha256. That makes the raw JSON suitable for
archiving as milestone evidence even before a visual viewer or PDF/HTML export
exists. Default output stays byte-deterministic; pass --generated-at now or
an explicit timestamp when an archived copy should embed a generation time.
Schema support: IFC4 and IFC2X3, same-schema comparisons only (no IFC2X3↔IFC4 translation).
Installation
pipx install athar-ifc
This installs the athar command. The PyPI distribution is athar-ifc; the
Python packages import as athar, athar_git, and so on. Wheels ship the Rust
signature pipeline (athar_native) and the viewer bundle for Linux (x86_64,
aarch64), macOS (arm64, x86_64), and Windows (x86_64); other platforms build
from the sdist, which needs a Rust toolchain. Requires Python 3.10+ and
ifcopenshell.
Local development (needs a Rust toolchain):
python -m venv .venv
source .venv/bin/activate
make dev-setup
make native-build # builds athar_native and installs Athar editable
make test
Usage
# Two-file diff (JSON output)
python -m athar old.ifc new.ifc
# Include an audit generation timestamp for an archived report
python -m athar old.ifc new.ifc --generated-at now
# CI policy gate over a fresh diff; exits 2 on policy violations
python -m athar check old.ifc new.ifc --policy athar-policy.json
# CI policy gate over an existing JSON report
python -m athar check --report report.json --policy athar-policy.json
# Stream output as NDJSON records (header, one record per entity, end with stats)
python -m athar old.ifc new.ifc --stream ndjson
# Stream output as chunked JSON
python -m athar old.ifc new.ifc --stream chunked_json --chunk-size 1000
CI Policy Gates
athar check evaluates an Athar JSON report against a small JSON policy and
prints structured JSON: { "ok": bool, "violations": [...], "summary": ... }.
It exits 0 when the policy passes, 2 when the diff is valid but violates
policy, and 1 for execution/configuration errors.
Example policy:
{
"forbid_schema_change": true,
"max_deleted": 0,
"max_deleted_by_class": {
"IfcWall": 0
},
"max_modified_change_scope": {
"intrinsic": 10,
"mixed": 0
},
"forbid_site_placement_change": true,
"max_placement_delta_mm": 50,
"max_placement_delta_mm_by_class": {
"IfcSite": 0
},
"forbid_aspect_changes": {
"data": ["IfcWall"],
"placement": "*"
}
}
The gate is report-driven and CI-platform-agnostic. Current policies can only
inspect signals present in the report: schema equality, section counts,
class-specific counts, modified change scopes, changed aspects, and placement
translation deltas. Fine-grained checks such as “property FireRating was
removed” require future report detail beyond the current data hash.
Git IFC Diff Driver
Athar can be installed as a Git diff driver for .ifc files. The driver uses
the same semantic engine, renders a deterministic terminal summary, and caches
signature bundles on disk by Git blob id (falling back to a content hash for
working-tree files).
# Configure the current repository and add .gitattributes guidance
athar git install
# Manual terminal rendering without Git plumbing
athar git diff old.ifc new.ifc
athar git install sets diff.athar.command to
athar git diff --external and appends:
*.ifc diff=athar -merge
The -merge marker is intentional. IFC is STEP text, but Git's default text
merge can silently splice two exported models into a corrupt file with
duplicate or dangling STEP ids. Athar provides semantic diffing; it does not
attempt semantic merge.
The persistent signature cache is versioned and deterministic, but v1 does not
evict old entries. Set ATHAR_CACHE_DIR to isolate or periodically clear a
large repository's cache.
GitHub PR Comments
Athar also includes a thin GitHub PR bot command for CI. It discovers .ifc
files changed between two Git refs, runs the same cached semantic diff for
modified/renamed pairs, and creates or updates one stable PR comment marked by
an HTML sentinel.
athar git pr-comment \
--base "$BASE_SHA" \
--head "$HEAD_SHA" \
--repo owner/repo \
--pr 123
Use --dry-run to print the Markdown comment body without calling GitHub.
When --policy-result athar-check.json is supplied, the comment includes a
pass/fail summary from an athar check result, but policy enforcement remains
separate. The checked-in .github/workflows/athar-pr-diff.yml workflow shows
the default pull-request setup using GITHUB_TOKEN.
Visual 3D Diff Viewer
athar view overlays both models in one 3D scene in the browser, colored by
the semantic report: green added, red deleted, blue modified, gray ghosted
unchanged, amber displacement lines for placement moves. An old↔new slider
crossfades between revisions; clicking an entity shows its class, GUID,
changed aspects, placement delta, and match evidence.
athar view old.ifc new.ifc # build report, serve, open browser
athar view old.ifc new.ifc --no-open --port 4799 # print the URL instead
athar git install --difftool # then: git difftool -t athar -- model.ifc
The browser does all tessellation (@ifc-lite/geometry WASM); rendering is
three.js. --viewer-url (or ATHAR_VIEWER_URL) hands off to a hosted viewer
Perfetto-style: the page fetches the models back from the local server via
?src=, so nothing is uploaded. Offline mode serves a local build from
ATHAR_VIEWER_DIST, a release-packaged athar_view/static, or a freshly built
viewer/dist. The same SPA also accepts plain drag-drop of old.ifc,
new.ifc, and a report JSON — no launcher needed.
Viewer development requires bun and lives entirely under
viewer/ (see viewer/README.md). Build the local bundle
with make viewer-build; make viewer-package-static copies it into
athar_view/static, which the release workflow does before building wheels.
Testing
make test # full default suite (~20 seconds)
make test-large-acceptance # opt-in large IFC acceptance checks
The default suite runs corpus-wide invariant tests (same-file zero diff,
stats accounting, no cross-class matches, duplicate-GUID and dangling-ref
behavior, schema policy) over every small real IFC in the corpus — the
Building-Landscaping v0→v3 revision chain, the IFC2X3 Duplex architecture
model, two GNI BIM Fundamentals samples, and small external samples — plus
metamorphic
GUID-scramble tests that prove identity recovery without GlobalId evidence.
It also generates known-edit semantic scenarios from real seeds (GUID
scramble, moving one product by a known vector, deleting a leaf product,
editing a pset value, editing a type-level pset value inherited by its
occurrences, renaming, duplicating a GUID) in temp dirs and asserts
the report against expectations derived from the constructed edit itself,
never from blessing engine output. Small files from the optional external
corpus (default ../vscode-ifc/test-files, override via
ATHAR_EXTERNAL_CORPUS_DIR) skip when absent.
Large acceptance checks are opt-in so day-to-day runs stay fast:
ATHAR_RUN_LARGE_ACCEPTANCE=1 python -m pytest tests/test_acceptance_large_ifc.py -q
The acceptance tier covers the medium/large corpus (8MB–182MB; same-file
invariants, a real 44MB revision pair, discipline pairs, unrelated pairs, and
a GUID-scramble at scale). Files that are missing or unfetched LFS pointers
skip individually. Bound wall-clock per test via ATHAR_ACCEPTANCE_TIMEOUT_S.
At every tier, tests assert structural invariants and expectations derived
from the inputs themselves — never exact whole-report JSON goldens. Corpus
files too big for the repo will be described by a checked-in manifest rather
than stored (design: docs/corpus/REMOTE_CORPUS_MANIFEST.md; not yet
implemented).
Documentation
Agent-facing architecture and conventions are split by ownership: start at
AGENTS.md, then follow the nearest subdirectory AGENTS.md for
core engine, Git integration, tests, docs, and scripts. Dated performance
findings live under docs/perf/, with docs/perf/STATUS.md stating what is
still current. Corpus survey findings live under docs/corpus/.
License
MIT
Metadata
Release files for athar-ifc 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| athar_ifc-0.1.0.tar.gz | 922.5 kB | Without provenance | Details for athar_ifc-0.1.0.tar.gz |
Built distributions (wheels)
| File | Actions Reset | |||
|---|---|---|---|---|
| athar_ifc-0.1.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details for athar_ifc-0.1.0-cp310-abi3-win_amd64.whl |
| athar_ifc-0.1.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details for athar_ifc-0.1.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl |
| athar_ifc-0.1.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details for athar_ifc-0.1.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl |
| athar_ifc-0.1.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details for athar_ifc-0.1.0-cp310-abi3-macosx_11_0_arm64.whl |
| athar_ifc-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details for athar_ifc-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl |
Total release size: 7.8 MB
Release files / athar_ifc-0.1.0.tar.gz
| Download URL | athar_ifc-0.1.0.tar.gz |
|---|---|
| Size | 922.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca4345586f8bcaf82ae1fa781c0fa9ee08ee125c84cb9b058dcd5fe792315c82
|
|
BLAKE2b-256 checksum How to use checksums |
de2109c00d52c749bbb7e4ff715440a06dd054d91e7ebd3f647e86775c881081
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / athar_ifc-0.1.0-cp310-abi3-win_amd64.whl
| Download URL | athar_ifc-0.1.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
b452052e54d8e503762967e09c40b0619547b8dc0b188b2d26b091691679af0d
|
|
BLAKE2b-256 checksum How to use checksums |
b559cacc25a0cd569bfddade9dd6bb7aecfd89fac867fd654ca24867bc437d5d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / athar_ifc-0.1.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
| Download URL | athar_ifc-0.1.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
e8b392a1d719aafd22a802452fe6bdf0f5a94d4576f205fa5fe4ee5c9c93b19a
|
|
BLAKE2b-256 checksum How to use checksums |
f7908a84a711325ab5e0ffd68b602dd21fecefdae691d0e875a7061256a7e134
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / athar_ifc-0.1.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
| Download URL | athar_ifc-0.1.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
c7b1cc2c3999cad7ef1d5786bc720a93a938cce7259a0356f7292a76eeb05ff6
|
|
BLAKE2b-256 checksum How to use checksums |
e46a451239f7bf0b4adc08b81e4219f5076cda6dcdbb2bd0dcb2f98f58bef92b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / athar_ifc-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | athar_ifc-0.1.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c9bb14b25608fc9c865ca6fe912002dba434f1de50e036cc524fa66046bb6eb7
|
|
BLAKE2b-256 checksum How to use checksums |
971d7862a4fa15579388bf7125905ec0252416c1dc29949ab544d3c91773a90d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / athar_ifc-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | athar_ifc-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
d938d6e5b56fdf7cc924c0ad6347cb39375a74263aa6c090b6c76492d10cf81e
|
|
BLAKE2b-256 checksum How to use checksums |
74044d9352f9282a9911fabf015588a5d7190416a00c98c6abf62ff0ff87ed71
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for athar-ifc 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.