Skip to content

fix(core): IFCELECTRICDISTRIBUTIONPOINT was spelled IFCELECTRICAL- in four places - #3185

Open
BIMvoice wants to merge 2 commits into
mainfrom
fix/legacy-electric-distribution-point
Open

fix(core): IFCELECTRICDISTRIBUTIONPOINT was spelled IFCELECTRICAL- in four places#3185
BIMvoice wants to merge 2 commits into
mainfrom
fix/legacy-electric-distribution-point

Conversation

@BIMvoice

Copy link
Copy Markdown
Collaborator

Refs #3172 — the typo half of it, which is unambiguous. The larger gap in the same table needs a mapping decision and is untouched here.

IfcElectricDistributionPoint is a real, concrete IFC2X3 entity — IfcFlowController, carrying ObjectPlacement and Representation. Every place that claimed to handle it spelled it IfcElectricalDistributionPoint, with an "AL" that no IFC schema has.

Four sites, two languages, all agreeing with each other

site what it did
rust/core/src/legacy_entities.rs the map key — the arm could never match a real file
rust/core/src/schema_helpers_tests.rs asserted has_geometry_by_name on the misspelling
packages/parser/src/ifc-schema.ts ENTITY_NAME_ALIASES carried the same misspelled key
packages/parser/test/known-type-across-schemas.test.ts asserted it resolves, calling it "real, deprecated IFC2X3 syntax"

The TS alias comment says it mirrors legacy_entities.rs. It did — typo included.

The consequence: the real entity missed the table, fell through to IfcType::from_strUnknown, and lost the has_geometry: true that arm exists to give it. The meshing pass then skipped it, silently. Nothing failed, because both languages' tests asserted a name that cannot occur, and both passed against tables misspelled the same way.

That is why "the tests are green" was not evidence here — the oracle shared the defect.

Verified both directions

cargo test -p ifc-lite-core --lib   →  161 passed, 0 failed

Restoring the typo fails electric_distribution_point_is_spelled_as_the_step_keyword with "IFCELECTRICDISTRIBUTIONPOINT is a real IFC2X3 keyword and must resolve". The new tests assert the misspelling is absent as well as the real name present, so re-adding it as an extra arm cannot quietly satisfy them.

Two things deliberately not done

The TS alias entry is kept, not deleted — though on that side it is redundant. ENTITIES_IFC2X3 does carry IfcElectricDistributionPoint, so getInheritanceChain reaches IfcDistributionElement through its real parents (IfcFlowControllerIfcDistributionFlowElementIfcDistributionElement) with no alias at all. It stays to keep lockstep with the Rust core, whose generated enum is IFC4X3-only and genuinely needs it. The comment there claimed no bundled table carries the entity — false for the corrected name — and is fixed too.

base_type stays IfcDistributionElement. The real parent is IfcFlowController, which exists in the Rust schema and would be more precise, but changing it changes classification for a live entity. That is a separate decision from repairing a name, and yours to make.

Verification gap, stated plainly

The TS side is not verified locally: this worktree has no node_modules, and the one that had them lost its .git to an unrelated prune mid-session. The TS change is a key rename plus its matching test, and the parent chain quoted above was read out of the bundled table rather than assumed. CI runs those tests.

No changeset: has_geometry for a previously-unreachable key is a bug fix with no API change, and the packages affected are internal to the parse path — happy to add one if you would rather it appear in the notes.

…erywhere

`IfcElectricDistributionPoint` is a real, concrete IFC2X3 entity
(`IfcFlowController`, carrying ObjectPlacement and Representation). Every
place that claimed to handle it spelled it `IfcElectricalDistributionPoint`,
with an "AL" no IFC schema has. Four sites, two languages:

- rust/core/src/legacy_entities.rs: the map key, so the arm could never match
  a real file and the entity fell through to IfcType::from_str -> Unknown,
  losing the has_geometry: true that arm exists to give it. The meshing pass
  then skipped it silently.
- rust/core/src/schema_helpers_tests.rs: asserted has_geometry_by_name on the
  misspelling. It passed -- against the table key that was wrong the same way.
- packages/parser/src/ifc-schema.ts: ENTITY_NAME_ALIASES carried the same
  misspelled key, its own comment saying it mirrors legacy_entities.rs. It
  did mirror it, typo included.
- packages/parser/test/known-type-across-schemas.test.ts: asserted the
  misspelling resolves, describing it as "real, deprecated IFC2X3 syntax". It
  is not syntax at all.

So the two sides were in lockstep and both tests were green, on a name that
does not exist, while the entity that does exist got nothing. This is why
"the tests pass" was not evidence here: the oracle shared the defect.

Verified both directions on the Rust side:
  cargo test -p ifc-lite-core --lib  ->  161 passed, 0 failed.
  Restoring the typo fails electric_distribution_point_is_spelled_as_the_step_keyword
  with "IFCELECTRICDISTRIBUTIONPOINT is a real IFC2X3 keyword and must resolve".

The new tests assert the misspelling is ABSENT as well as the real name
present, so re-adding it as an extra arm cannot quietly satisfy them.

Two things deliberately NOT done:

The TS alias entry is kept rather than deleted, though on that side it is
redundant: ENTITIES_IFC2X3 carries IfcElectricDistributionPoint, so
getInheritanceChain reaches IfcDistributionElement through its real parents
(IfcFlowController -> IfcDistributionFlowElement -> IfcDistributionElement)
without any alias. The alias exists to stay in lockstep with the Rust core,
whose generated enum is IFC4X3-only and genuinely needs it. The comment there
claimed no bundled table carries the entity, which was false for the corrected
name; that premise is now fixed too.

base_type stays IfcDistributionElement. The real parent is IfcFlowController,
which does exist in the Rust schema and would be more precise, but changing it
changes classification for a live entity and is a separate decision from
repairing a name.

TS side is not verified locally: this worktree has no node_modules and the one
that had them lost its .git to an unrelated prune. The TS change is a key
rename plus the matching test, and the parent chain above was read from the
bundled table rather than assumed. CI runs those tests.

Refs #3172, which reported this typo alongside a larger gap in the same table
that needs a mapping decision and is not touched here.
@BIMvoice
BIMvoice requested a review from louistrue as a code owner August 25, 2026 04:38
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 20 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 846a34d1-ae28-45c4-917e-5b86906aeb05

📥 Commits

Reviewing files that changed from the base of the PR and between ec35816 and b4d6e64.

📒 Files selected for processing (7)
  • packages/parser/src/ifc-schema.ts
  • packages/parser/test/known-type-across-schemas.test.ts
  • packages/query/src/ifc-query.ts
  • packages/query/test/oftype-unknown-type.test.ts
  • rust/core/src/legacy_entities.rs
  • rust/core/src/schema_helpers.rs
  • rust/core/src/schema_helpers_tests.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Viewer benchmark

✅ No threshold regressions detected.

01_Snowdon_Towers_Sample_Structural(1).ifc

Baseline recorded 2026-07-01T20:31:05.538Z on github-actions ubuntu-latest, viewer-benchmark-ci (headless Chrome, SwiftShader ANGLE), production build.

Metric Current Baseline Delta Threshold Status
firstBatchWaitMs 1466ms 2905ms -49.5% +50%
firstVisibleGeometryMs 1893ms 3652ms -48.2% +50%
streamCompleteMs 2445ms 3598ms -32.0% +50%
spatialReadyMs 1133ms 1032ms +9.8% +50%
metadataCompleteMs 1453ms 3063ms -52.6% +50%
totalWallClockMs 2500ms 3700ms -32.4% +50%

AC20-FZK-Haus.ifc

Baseline recorded 2026-07-01T20:30:59.972Z on github-actions ubuntu-latest, viewer-benchmark-ci (headless Chrome, SwiftShader ANGLE), production build.

Metric Current Baseline Delta Threshold Status
firstBatchWaitMs 252ms 1075ms -76.6% +50%
firstVisibleGeometryMs 985ms 1572ms -37.3% +50%
streamCompleteMs 1089ms 1980ms -45.0% +50%
spatialReadyMs 861ms 915ms -5.9% +50%
metadataCompleteMs 1002ms 1392ms -28.0% +50%
totalWallClockMs 1400ms 3300ms -57.6% +50%

Refresh the baseline from a CI run: dispatch the Benchmark workflow with record_baseline, download the benchmark-baseline artifact, and commit baseline.json (see tests/benchmark/README.md).

CI caught what I could not run locally. packages/query's
oftype-unknown-type.test.ts asserted that ofType('IfcElectricalDistributionPoint')
does not throw -- and once the alias key was corrected, it did.

That test is a fifth site of the same typo, and its comment was wrong twice
in one sentence: it called the name "an IFC2X3 leaf that no bundled EXPRESS
export carries" and said ENTITY_NAME_ALIASES "is the only table that knows
it". The name is not an IFC entity at all, so the only thing that knew it was
an alias key misspelled to match; and the real IfcElectricDistributionPoint IS
in ENTITIES_IFC2X3. Verified the corrected name belongs in STANDARD_BUT_UNMAPPED
on its own merits: it is a known type (so ofType does not throw) and has no
TYPE_STRING_TO_ENUM entry (so it resolves to Unknown), which is exactly what
that list is for.

A sixth site: ifc-query.ts's paragraph explaining why the alias table is
consulted used the misspelling as its example of "names real STEP files carry
that the bundled EXPRESS exports omit". It was not an example of that -- the
real entity is in the bundled export -- so the paragraph now says so instead of
illustrating itself with a name that cannot occur.

Not touched: packages/parser/CHANGELOG.md and packages/query/CHANGELOG.md both
carry the misspelling, including a whole released entry titled "Resolve
IfcElectricalDistributionPoint's inheritance chain in the TS parser" (#2883).
Those are published release notes -- a record of what shipped, typo included.
Rewriting history to match a later correction is not the same as fixing the
code, and #2883 is where this thread starts if anyone wants the origin.

Six sites, two languages, and the count kept going up because each fix exposed
the next: the Rust table, a Rust test, the TS alias map, a TS parser test, a TS
query test, and a TS source comment. Every one agreed with the others, and the
suites were green throughout.

cargo test -p ifc-lite-core --lib: 161 passed. The TS half still cannot run
here (no node_modules); CI is the check, which is what found this one.
@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
ifc-lite-dev Ignored Ignored Preview Aug 25, 2026 4:59am
ifc-lite-viewer-embed Ignored Ignored Aug 25, 2026 4:59am

@louistrue

Copy link
Copy Markdown
Collaborator

Petru, this collided with #3178 and that is on me, not on you. #3178 merged as 224386ac about 17 minutes after you opened this, and I merged it without checking whether an open PR was addressing the same defect. You found a real bug and the fix landed under someone else's PR number.

You identified the same root cause independently: the IfcElectricDistributionPoint key was spelled with an "AL" that no IFC2X3 entity has, so the row could never match a real file, and a test asserted against the same misspelling so the table and its test certified each other.

State on 224386ac, checked rather than assumed:

rust/core/src/legacy_entities.rs:123   "IFCELECTRICDISTRIBUTIONPOINT" => Some(...)
rust/core/src/schema_helpers.rs:76     correct spelling in the docs
schema_helpers_tests.rs:118            assert!(has_geometry_by_name("IFCELECTRICDISTRIBUTIONPOINT"))
schema_helpers_tests.rs:124            assert!(!has_geometry_by_name("IFCELECTRICALDISTRIBUTIONPOINT"))

A case-insensitive sweep of electricaldistributionpoint across packages/ rust/ apps/ returns 12 hits and no live site: published CHANGELOG entries, comments recording what the name was until #3172, and two deliberate negative guards so restoring the bad spelling turns tests red.

Comparing the arm tables directly, main has 26 and this branch has 22, and the only key here that main lacks is "IFCELECTRICALDISTRIBUTIONPOINT" itself.

That difference is the one real design choice between the two, and it is a preference rather than a gap. You respelled the parser alias as belt and braces; main removed the row instead, on the reasoning that the correctly spelled entity is already in ENTITIES_IFC2X3 so ENTITY_INFO_BY_UPPER resolves it through its real parents and an alias would be a second, shadowing answer. Your own comment notes the same fact about getInheritanceChain reaching it without the alias. Both readings are correct about the mechanism.

So there is nothing in this branch that main is missing, and rebasing it would mean resolving a conflict in a table both changes rewrote, for no remaining delta.

What to do with this PR is your call, or Louis's. I have not touched it. If you would rather the credit sit where the work happened, I am happy to open a follow-up that records your finding, or to say so on #3178.

One thing from #3178 that may interest you given how this bug hid: scripts/check-legacy-entity-coverage.mjs now runs on every PR and fails in both directions, so a concrete legacy product with no arm fails, and an arm whose key names no entity in any bundled schema also fails. The misspelling class cannot come back silently now.

@BIMvoice

Copy link
Copy Markdown
Collaborator Author

Superseded — this is already fixed on main, and better than I had it. Flagging rather than closing; the call is yours.

main carries the correction at all six sites, with comments citing #3172:

  • rust/core/src/legacy_entities.rs — the table key is IFCELECTRICDISTRIBUTIONPOINT
  • rust/core/src/schema_helpers.rs, schema_helpers_tests.rs
  • packages/parser/src/ifc-schema.ts, packages/parser/test/known-type-across-schemas.test.ts
  • packages/query/src/ifc-query.ts, packages/query/test/oftype-unknown-type.test.ts

And it pins both directions, which is the part I would have argued for:

assert!(has_geometry_by_name("IFCELECTRICDISTRIBUTIONPOINT"));
assert!(!has_geometry_by_name("IFCELECTRICALDISTRIBUTIONPOINT"));

plus the TS equivalent in known-type-across-schemas.test.ts, where the real name resolves through IfcFlowController / IfcDistributionElement and the misspelling returns []. That is the same two-way assertion my branch added, so there is nothing here worth rescuing on that front.

This branch is CONFLICTING against main, and the conflicts are in exactly those six files — the two fixes colliding, not a rebase problem. That is also why no test workflow ever ran on it: only Vercel and CodeRabbit reported, so this PR has never actually been tested. Worth knowing in general — a conflicting PR looks like it has a check list, and none of the real jobs are in it.

One item that does not appear to exist on main: a test asserting the repaired key is absent from the generated schema, i.e. that the legacy table is not shadowing a generated entry for the same keyword. That is a different property from "the name resolves", and I did not find it pinned anywhere. If you want it, I will open a small PR with just that test and nothing else. Otherwise this one can go.

@BIMvoice

Copy link
Copy Markdown
Collaborator Author

Thanks for tracing it rather than just closing it — and no need to apportion anything. Two people finding the same bug within twenty minutes is a good sign about the sweep, not a problem.

I checked the one item I had flagged as possibly missing, and it is not missing — it is mechanized, which is better than what I was offering. My branch carried a unit test asserting the repaired key is absent from the generated schema, i.e. that the legacy table is not shadowing a generated entry. scripts/check-legacy-entity-coverage.mjs on main does that as a gate:

LEGACY_REL has an arm for "${key}", which names no entity in any bundled schema table —

exiting 1, wired into test.yml on every PR, with its own test file beside it. A gate that runs on every PR beats a unit test in one crate, and it catches the whole class rather than the one key. So there is genuinely nothing left in this branch.

On the alias: I agree it is preference, and on reflection I think main chose the better one. My belt-and-braces respelling adds a second answer for a name ENTITY_INFO_BY_UPPER already resolves through its real parents, and two paths that must agree is the shape that produced this bug in the first place — the table and its test certifying each other. Removing the row leaves one answer, which is the property worth having.

I would rather this PR just go, and I have no interest in a follow-up recording the finding — the fix is in and the gate is in, which is the part that mattered. I do not close PRs myself, so it is yours to close whenever convenient.

The coverage gate is the useful outcome here. The misspelling survived because nothing could observe it: the row could never match a real file, and the only test pointed at the same wrong string. Failing in both directions is exactly what that needed.

@louistrue

Copy link
Copy Markdown
Collaborator

Checked this at head: fail=0, pending=0, but it needs a real rebase, not a mechanical one. Six files conflict, seven markers:

packages/parser/src/ifc-schema.ts                        1
packages/parser/test/known-type-across-schemas.test.ts   1
packages/query/src/ifc-query.ts                          1
packages/query/test/oftype-unknown-type.test.ts          1
rust/core/src/legacy_entities.rs                         2
rust/core/src/schema_helpers_tests.rs                    1

These are content conflicts in the legacy-entity tables themselves, on both the Rust and TypeScript sides, so this is not a pin or a digest that can be regenerated. Worth resolving by hand with the schema open.

Given what this PR is fixing, one specific caution. A misspelling in this family has already propagated once: the same wrong keyword was mirrored from Rust into TypeScript, reaching five artifacts for an entity that does not exist. So when you resolve, check the two sides agree on the corrected spelling rather than each keeping its own, since a conflict resolution that takes "ours" on one side and "theirs" on the other is exactly how the halves drifted apart in the first place. If there is a test that asserts the Rust and TS tables match, that is the one to run after resolving.

I have not touched your branch. Ping me once it is updated and I will review and merge.

@louistrue

Copy link
Copy Markdown
Collaborator

This one is conflicted against main and needs you rather than a mechanical resolution, so flagging rather than guessing.

I have been clearing the allowlist conflicts across tonight's batch, but this PR is a different shape. Seven conflict hunks, and every one has content on both sides:

packages/parser/src/ifc-schema.ts                    1 hunk, 1 two-sided
packages/parser/test/known-type-across-schemas.test.ts   1 hunk, 1 two-sided
packages/query/src/ifc-query.ts                      1 hunk, 1 two-sided
packages/query/test/oftype-unknown-type.test.ts      1 hunk, 1 two-sided
rust/core/src/legacy_entities.rs                     2 hunks, 2 two-sided
rust/core/src/schema_helpers_tests.rs                1 hunk, 1 two-sided

The ones I have been resolving were either the pinned-digest file (mechanical, and I re-derive the numbers from the gate rather than picking) or one-sided hunks where one branch adds and the other does not — those are determined rather than judged. None of these are. Both sides changed the same legacy-entity name handling, and choosing between them is a call about schema-name semantics in your code, not a merge mechanic. Resolving it blind is exactly how a correct-looking merge quietly drops one side's intent.

It has also been open since 04:38 and main has moved a long way, so a rebase may be less painful than a merge here.

Two things that may help when you pick it up:

  • rust/core/src/legacy_entities.rs is where most of the two-sidedness is (2 of the 7 hunks), and it now has a parity gate against the TS side, so whichever way you resolve it the Rust and TS lists have to agree or the gate will say so.
  • The rest of tonight's batch has landed or is unblocked, so this should be the last one fighting main.

Happy to resolve it if you tell me which side wins in legacy_entities.rs — the mechanical half after that is straightforward.

@louistrue

Copy link
Copy Markdown
Collaborator

Do not merge this yet. It reads MERGEABLE with zero failures, and it has never been compiled, typechecked, linted or tested.

This PR carries 8 checks. A healthy PR on this repo carries 38. The missing lanes:

Typecheck                     Lint
Node tests                    Rust tests
Build packages + WASM         Build + WASM + Rust + Node   (the required aggregate)
Viewer tests (shards 0-3)     Viewer E2E smoke
Rust crate semver             Detect changes
... 30 lanes in total

What it does have is parity, three Vercel deployments, Vercel Agent Review, CodeRabbit and Vercel Preview Comments. None of those builds the code.

Cause: test.yml never fired. The PR was opened against a feature branch, and retargeting it to main does not fire workflows retroactively. So the checks that exist are the ones that ran against the original base, and the whole test matrix simply never started.

Why no failure count catches this. The required aggregate is not red, it is absent. fail=0 is literally true, and so is mergeable. Anything asking "is anything failing?" gets the answer "no" and is correct — it is just answering a different question from "has this been tested?".

The fix is one push of any kind — an empty commit, a rebase onto current main, anything creating a new head event now that the base is main. That fires the full matrix. I have not pushed it myself; it is your branch.

Worth doing before that push, since the base changed: this has never been merged against current main, which has moved a long way today (#3298's semver gate, #3306's sharded module-size digest, and eleven other merges). A rebase rather than an empty commit will tell you whether it still applies, not just whether it compiles in isolation.

#3185 has the identical shape — 6 checks, same missing set — but it is CONFLICTING so it cannot merge by accident. Same treatment when it is resolved.

For anyone sweeping the board: count the checks before reading their status. gh pr view <n> --json statusCheckRollup --jq '.statusCheckRollup|length'. Under ~15 on a non-fork PR means a workflow never ran, and no amount of reading the green ones reveals it.

@louistrue

Copy link
Copy Markdown
Collaborator

Conflicting, and about half of it has landed by another route — worth a look before you spend time rebasing 108 commits.

The spelling fix is already on main, via #3172. The misspelling is gone from all live code; what remains are deliberate history comments:

packages/parser/src/ifc-schema.ts:45   // There was a fourth row here, `IFCELECTRICALDISTRIBUTIONPOINT`. The …
rust/core/src/legacy_entities.rs:117   // Spelled IFCELECTRICALDISTRIBUTIONPOINT until #3172. There is no such …
rust/core/src/schema_helpers_tests.rs  assert!(!has_geometry_by_name("IFCELECTRICALDISTRIBUTIONPOINT"));

That last one is a negative assertion pinning the misspelling as not matching, which is the right shape and already there.

But this PR is not purely superseded. Comparing your head against current main, rust/core/src/legacy_entities.rs still carries roughly 40 non-comment lines main does not have, and packages/query/src/ifc-query.ts about 10. rust/core/src/schema_helpers.rs looks fully covered. (Rough count from a sorted-line comparison, so read it as "there is something left here", not an exact number.)

So the useful question is whether that remainder is still wanted on its own. If it is, a rebase should shrink this PR a lot — the spelling changes will drop out as already-applied and what is left is the legacy-entity coverage.

Merge-base is ec358164, 108 commits behind. Nothing here is a comment on the change itself.

@BIMvoice

Copy link
Copy Markdown
Collaborator Author

Checked the remainder file by file against main — nothing here is still wanted, and one piece would actively regress.

legacy_entities.rs — main sets base_type: IfcType::IfcFlowController; this branch keeps IfcDistributionElement. IfcFlowController is the entity's own IFC2X3 supertype and is still a subtype of IfcDistributionElement, so main's is strictly more precise. Landing mine would walk that back.

packages/parser/src/ifc-schema.ts — main removed the row rather than respelling it, and the comment there gives the reason: the correctly spelled name is already in ENTITIES_IFC2X3, so ENTITY_INFO_BY_UPPER resolves it and an alias would be "a second, shadowing answer". My branch respells and keeps it, with a comment saying it is kept "so the two files stay in lockstep". That is the two-paths-that-must-agree shape that produced this bug in the first place. Main is right to let Rust and TypeScript differ in mechanism — Rust via a legacy-table arm because the generated enum is IFC4X3-only, TypeScript via the real IFC2X3 inheritance chain — while agreeing in result.

The in-crate Rust tests are subsumed by scripts/check-legacy-entity-coverage.mjs, which gates every arm in both directions on every PR rather than pinning one key. A gate beats a unit test here, as you said on the earlier thread.

The rest is comments, already rewritten on main — including the ifc-query.ts one, where main swapped in a working example and explicitly noted the old citation was wrong.

So: nothing to rescue, and I would not resolve the conflicts. Leaving it open rather than closing — your call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants