fix(export): derive rooted-type detection from the schema, matching the Rust side - #3030
Conversation
… denylist with a schema check
extractGlobalIdFast identified a rooted entity's GlobalId positionally, gated
by NON_ROOTED_STRING_TYPES — a hand-maintained denylist of non-rooted types
that lead with a Name/Identifier string. The list was incomplete
(IfcMaterialProfileWithOffsets and other resource types were missing), so a
merge could misread such an entity's Name as a GlobalId and, on a coincidental
collision, silently unify it away — corrupting ordinary model data.
Replaced the denylist with a schema-derived positive check,
getInheritanceChainAcrossSchemas(type).includes('IfcRoot'), mirroring the Rust
exporter's IfcType::is_subtype_of(IfcRoot) so the two sides agree on what
"rooted" means instead of keeping separate hand-maintained answers.
|
Warning Review limit reached
Next review available in: 54 minutes 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. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe merged STEP exporter now uses IFC schema inheritance to identify ChangesMerged export rooted-entity detection
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The exporter now derives rooted-type detection from the schema, aligning GUID reconciliation with the Rust implementation, with regression coverage for rooted collisions and material-name handling. No actionable merge-blocking risk remains beyond normal checks and review. Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
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. Comment |
Viewer benchmark✅ No threshold regressions detected. 01_Snowdon_Towers_Sample_Structural(1).ifcBaseline recorded 2026-07-01T20:31:05.538Z on github-actions ubuntu-latest, viewer-benchmark-ci (headless Chrome, SwiftShader ANGLE), production build.
AC20-FZK-Haus.ifcBaseline recorded 2026-07-01T20:30:59.972Z on github-actions ubuntu-latest, viewer-benchmark-ci (headless Chrome, SwiftShader ANGLE), production build.
Refresh the baseline from a CI run: dispatch the Benchmark workflow with |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Merge review: approved. Schema-derived rootedness replaces the hand-maintained denylist correctly.
Three mutations run, three killed, rather than reasoned about:
That last pair matters: a one-directional assertion would be satisfied by a mutation that hard-codes the answer, and both directions failing is what rules that out. The pre-existing property and text-literal tests now exercise the schema path as well. Changeset present. 18 checks pass, 0 fail, 0 cancelled, on the current CI lane. Non-blocking note, recorded rather than acted on: |
|
@coderabbitai review Requesting the review that never ran: the check on this PR reports pass, but the bot's only comment here is the Fair Usage "Review limit reached" notice, so no code was looked at. Spending the reset quota on this one first because it is the closest to merge — @louistrue has already approved it by hand. |
|
Your plan includes PR reviews subject to rate limits. More reviews will be available in 44 minutes. |
Replaces the JS hand-maintained "non-rooted types" denylist with
getInheritanceChainAcrossSchemas(type).includes('IfcRoot')— the schema-derived positive check.This closes the divergence flagged on #3007. That PR moved the Rust exporter to a schema-derived allowlist (
IfcType::is_subtype_of(IfcRoot)) while JS kept a hand-kept denylist, so the two exporters could disagree about which entities get GUID-reconciled. After this, both answer the question from the same schema rather than from two lists someone has to keep in step.RED — surgical, reverting the call site and restoring the deleted 41-entry denylist constant, so it runs against the genuine old behaviour rather than a stub:
That test is two-way — it pins the exclusion and that real rooted collisions still reconcile. 771 pass after. Typecheck and all three lint gates clean.
A suspected "guard that cannot detect what it claims", chased and cleared
The old code upper-cased (
ref.type.toUpperCase()); the new one passes rawref.type, while STEP types are uppercase and the chain returns PascalCaseIfcRoot. Had the lookup been case-sensitive, every type would read as non-rooted and GlobalId reconciliation would have silently stopped repo-wide — and the changeset's own "safe-miss direction" reasoning would have masked it.Ran it rather than reasoning about it. The function normalises internally:
IFCWALL,IFCDOOR,IFCPROPERTYSET,IFCTASK,IFCGROUP,IFCWALLSTANDARDCASE→ rooted;IFCPROPERTYSINGLEVALUE,IFCMATERIAL,IFCPERSON,IFCTEXTLITERAL,IFCCLASSIFICATION,IFCQUANTITYLENGTH→ not rooted. All correct.The changeset's headline claim also checks out:
IFCMATERIALPROFILEWITHOFFSETSreads non-rooted and was genuinely absent from the old denylist.Disclosed, not a defect: a vendor-extension type subclassing
IfcRootyields an empty chain (IFCVENDORFOO→len=0) and is now treated non-rooted, losing a reconciliation it previously got. The changeset states this direction explicitly, and it is the safe one.🤖 Generated with Claude Code
Summary by CodeRabbit