Skip to main content

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 ObjectPlacement chain.

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)

Source distribution for athar-ifc 0.1.0
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)

Table of built distributions (wheels) for athar-ifc 0.1.0
File Actions
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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

6 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page