corenetx.md
Corpus version
3.1
License
CC-BY-4.0

Corpus crosscheck — 3.1 structural recapture (2026-07-28)

Human review artifact for the regeneration that replaced the migrated-zip capture with a parser reading the official IFC-SG glossary pages.

Before After
Components 74 74
IFC bindings 145 153
Parameters 687 723
Parameters with allowed_values 206 213
Parameters with reference_values 51 52
Input-limited parameters still awaiting values 3 4

How this was produced

  1. python scripts/parse_glossary.py — parses the structural layer (bindings, subtypes, S/N, property, type, applies_to, unit, input_limited, examples) out of the cached official glossary pages. Carries the non-derivable fields forward from the previous parameters.json (discipline, authorities, source*, page_range, header_kind, binding pages, and the editorial annotations example_format_acmv / prefix / glossary_key / glossary_mandatory).
  2. python scripts/resolve_enums.py — re-applies the value layer from _meta/enum_resolutions.json. The parser never writes allowed_values / reference_values.
  3. scratchpad/validate_against_cop.py — cross-validates every parsed property against the CoP v3.1 PDF (sha256 1f9f7197…fbc2a, now recorded in manifest.json).
  4. python scripts/validate.py → 0 errors, 0 warnings. python scripts/build.py twice → byte-identical output.

Anti-silent-drop contract. A dropped row is the failure that produced the previous capture's losses, so the parser checks for it three ways, two of them measured independently of the code doing the work:

All ten guards are verified against synthetic failing inputs on every change.

Those, plus any unparseable table or unrecognised Input Limitation value, abort the entire run with the component named. A partial or degraded file is never written. All three guards plus five others were verified against synthetic failing inputs (8/8).

Components added / removed

None. The official index lists 75 components; the corpus holds 74. The gap is fully explained and unchanged: Space Usage (Others) is not a separate component here — it is the page that publishes the 31-property IfcSpace / SPACE table that arc/space_usage already carries. The parser reads that component's structure from page d0a72875-f11a-4a53-9a1b-bfd84b9ea775 while keeping source_id on the parent Space Usage page (this is declared in arc/space_usage/parameters.jsonglossary_pages).

Properties added (36 net)

Component Added Source
Lift BarrierFreeAccessbility, ClearDepth, ClearHeight, ClearWidth, FireFightingLift, LiftType glossary page + CoP p.310 (2 → 8 properties)
Pile DA1-1_CompressionCapacity, DA1-1_CompressionDesignLoad, DA1-1_TensionCapacity, DA1-1_TensionDesignLoad, DA1-2_CompressionCapacity, DA1-2_CompressionDesignLoad, DA1-2_TensionCapacity, DA1-2_TensionDesignLoad, MinEmbedmentIntoBearingLayer_SPT_MoreThan_100N, MinEmbedmentIntoBearingLayer_SPT_MoreThan_60N glossary page + CoP pp.316–317
Footing / Pilecap DA1-1_BearingCapacity, DA1-2_BearingCapacity, WorkingLoad_DA1-1, WorkingLoad_DA1-2 glossary page + CoP p.295
Column WorkingLoad_DA1-1, WorkingLoad_DA1-2 glossary page + CoP p.269
Wall WorkingLoad_DA1-1, WorkingLoad_DA1-2 glossary page + CoP p.435
Staircase ConstructionMethod, MaterialGrade on the second IfcStair binding (15 → 17 params) glossary page
Road LoadingCapacity glossary page (carries footnote ^)
Tank CompactionRatio glossary page
Refuse Chute / Recyclables Chute NominalCapacity glossary page (carries footnote ^)
Space (Area Requirements) ACN_ConnectivityType (replaces the misspelled ACN_ConncectivityType) glossary page; the CoP PDF p.363 carries the same misspelling — the live page corrects it

Properties removed (1)

Component Removed Why
Pile Real Not a property. The previous capture mis-read two wrapped table rows and emitted a parameter literally named Real (property_raw: "Real MoreThan_100N MinEmbedmentIntoBearingLayer_SPT_"). Those rows now parse correctly as MinEmbedmentIntoBearingLayer_SPT_MoreThan_100N and …_60N.

Binding changes

Component Before After Reading
Accessible Route 1 binding, IfcSpace 5 bindingsIfcBuildingElementProxy, IfcSlab, IfcCivilElement, IfcRamp, IfcSpace (all ACCESSIBLEROUTE, same 2 properties) ⚠️ Live/CoP conflict — CoP p.251 lists only IfcSpace. The live page's banner names five entities; kept the live reading.
Space (Area Requirements) 1 merged binding, 23 params 5 bindings, 8+4+7+3+1 = 23 params Presentation change only. The page publishes five separate IfcSpace / AREA_GFA tables (AGF, AST, ACN, ALS, AVF); one binding per table keeps the official S/N verbatim instead of restarting it inside a single binding. prefix_groups still documents the five groups.
Fire Access Opening subtype OPENING ×3 subtype null ×3 ⚠️ Live/CoP conflict — the live SubType cell reads -; CoP p.287 reads OPENING.
Slab FLOOR, LANDING N.A., LANDING ⚠️ Live/CoP conflict — CoP p.353 reads N.A., FLOOR, LANDING; the live page omits FLOOR.
Window …, WINDOW, SKYLIGHT …, WINDOW ⚠️ Live/CoP conflict — CoP p.439 includes SKYLIGHT; the live page omits it.
Refuse Chute / Recyclables Chute IfcTank …, REFUSEHANDLINGEQUIPMENT IfcTank … (subtype dropped) ⚠️ Live/CoP conflict — CoP p.335 has it here. It now appears on the Tank page instead (next row).
Tank IfcTank …, SECTIONAL, VESSEL, … …, SECTIONAL, REFUSEHANDLINGEQUIPMENT, VESSEL, … ⚠️ Live/CoP conflict — CoP p.428 does not list it on Tank. Read together with the row above, the live pages appear to have moved this subtype.
Prefabricated Building Systems and MEP Components IfcSpace, 5 subtypes IfcSpace, 9 subtypes (+PREFABRICATEDANDPREFINISHEDWALL / FLOOR / CEILING, +PRECASTEXTERNALWALLWITHCAST-INWINDOWS) ⚠️ Live-only — the four added subtypes appear nowhere in the CoP v3.1 PDF.
Prefabricated Building Systems and MEP Components IfcPipeSegment/IfcDuctSegment carried RIGIDSEGMENT, FLEXIBLESEGMENT; the Fitting entities carried none IfcPipeFitting and IfcPipeSegment carry RIGIDSEGMENT, FLEXIBLESEGMENT; IfcDuctFitting/IfcDuctSegment carry none ⚠️ Recorded as published. The live page prints one SubType banner over a two-entity table (IfcPipeFitting, IfcPipeSegment), so both entities inherit it; the previous capture had editorially assigned the segment subtypes to the segment entity only. Needs a human rulingRIGIDSEGMENT/FLEXIBLESEGMENT are not IfcPipeFitting predefined types.
Railing BOLLARD, GUARDRAIL N.A., BOLLARD, GUARDRAIL Fix. CoP p.331 reads N.A., BOLLARD, GUARDRAIL; the previous capture dropped the leading N.A. alternative.
Road PARKINGACCESSWAY*, … PARKINGACCESSWAY, … Fix — the * was a footnote marker, not part of the subtype.
Fire Alarm subtypes alphabetised subtypes in page order Ordering only; same 5 values.

Multi-entity table banners (IFC Entity: IfcWall, IfcSlab) are split into one binding per entity, which is the convention the previous corpus already used for Fire Access Opening, Household Shelter, Pipes/Ducts and Prefabricated. Accessible Route is the only component where applying that convention uniformly changes the binding count.

Type / unit / input-limited changes

Component Property Field Before After Reading
Distribution Chamber InvertLevel, TopLevel type Real Text Live page and CoP agree on Text; also makes these consistent with the identical Interceptor fields.
Wall PrefinishedFacade type Boolean Boolean Live page prints a truncated Boole; normalised to Boolean per CoP p.435.
Space (Area Requirements) AVF_IncludeAsGFA type Boolean Boolean Live page prints Booelan; normalised per CoP p.373.
Tank CompactionRatio type (new property) Text ⚠️ Live/CoP conflict — the live Tank page states Text; the PDF states Real (p.335, under Refuse Chute). Recorded as published; see the subtype move above.
Refuse Chute / Recyclables Chute StructuralWidth, StructuralHeight, Thickness unit (none) mm Recovered — the previous capture had mis-aligned these rows (their examples read ["No"]).
Interceptor SystemType unit (none) mm ⚠️ Live page prints Unit mm on a Text SystemType row; the CoP has -. Recorded as published — looks like a page data error.
Site Boundary Area unit m2 Live page uses the superscript character.
Site Boundary BroadLandUse, VacantLand applies_to IFC SubType = SITEBOUNDARY only (dropped) The live table has no "Type of Elements" column; that text is a footnote, not an element scope.
Parking Lot / Pump / Window VehicleType, Duty, Standby, FireAccessOpening unit N.A. (dropped) N.A. is the page's "not applicable" marker, not a unit.
Pile 9 properties applies_to fragments (relevant, piles, All piles When required /) full values (when required / relevant, RC non-circular piles, All piles) Fix — the previous capture split wrapped cells across rows.
Staircase FireExit applies_to When required / relevant When required/ relevant Live page spacing, verbatim.
Roof ConstructionMethod (IfcSlab) input_limited true false Both the live page and CoP p.339 print Input Limitation = No, so the structural-enum list no longer applies as a hard constraint. ⚠️ The page is self-inconsistent (its example says "Refer to list*") — flagged for a human ruling.
System SystemType (all 15 bindings) input_limited true false Live page prints No. The 18 official System Types are now attached as reference_values (soft) rather than allowed_values (hard) — no values lost. ⚠️ Same self-inconsistency; needs a ruling.

examples were rebuilt for every component. The previous capture read the CoP PDF's multi-column example cells row-wise and produced garbled lists — Space (Usage) EmergencyVoiceCommunicationSystem was ["Address System."] and is now ["1-way EVC System", "2-way EVC System", "Public Address System."]; Column SpliceDetail was ["Detail 3 1st Storey", "F2"] and is now ["Detail 3"]; Space (Area Requirements) AGF_DevelopmentUse was a 25-item interleave of four PDF columns and is now the official 25-value list in page order. Example cells are stored split on commas, which render.py re-joins with ", ".

Blank cells on the live page, filled from the CoP PDF

A requirement cell holding - is the page saying "not applicable". A cell that is empty is a rendering defect, and the two must not be conflated: an earlier pass of this regeneration wrote type: null for Column Stirrups / StirrupsType because it treated them the same. The parser now separates them, and every empty cell must be answered by a CoP-sourced entry (LIVE_CELL_GAPS in scripts/parse_glossary.py, each quoting its PDF row) or the run aborts — a future recrawl cannot quietly open a new hole.

Six cells corpus-wide are blank. Two carry real values in the PDF; the other four are blank in the PDF too (-), so they resolve to "omitted" and change nothing:

Component Property Blank on the live page Filled from the CoP PDF row
Column Stirrups Property Type, Type of Elements, Unit type: Text, applies_to: RC column, unit omitted p.269 21 Stirrups Text RC column - No 4H13-300
Column StirrupsType Property Type, Unit type: Text, unit omitted p.269 22 StirrupsType Text When required / relevant - Yes Refer to list^
Distribution Chamber ID Examples omitted (PDF gives none) p.276 4 ID Text - No
Pile BoreholeRef Unit omitted (PDF -) p.316 1 BoreholeRef Text All piles - No BH2, BH3, BH12-2
Pile ConstructionMethod Unit omitted (PDF -) p.316 3 ConstructionMethod Text All piles - Yes Refer to list^
Pile ShaftR4DesignFactor Examples omitted (PDF -) p.317 2 ShaftR4DesignFactor Real when required / relevant - No -

Normalisations applied (the complete list)

Every one of these is a spelling/rendering defect where the CoP v3.1 PDF is unambiguous. Nothing else was changed silently.

lfcIfc (lower-case L rendered for capital I) — 21 IFC-entity banners across 15 components: Beam, Damper, Distribution Chamber, Interceptor, Pile, Sanitary Appliances, Space (Usage), Staircase, Tank, Type Bedding for Pipe, Valve, Wall, Waste Terminal, Water Meter, Window.

Live text Written Component CoP evidence
RIDIGSEGMENT RIGIDSEGMENT System / IfcPipeSegment PDF p.423
PWCSINPSECTIONCHAMBER PWCSINSPECTIONCHAMBER System / IfcDistributionChamberElement PDF p.425
RAODSIDEDRAIN ROADSIDEDRAIN System / IfcCivilElement PDF p.426
PLANTING AREAS PLANTINGAREAS Planting Areas PDF p.324
Shower SHOWER Sanitary Appliances PDF p.340
Boole Boolean Wall / PrefinishedFacade PDF p.435
Booelan Boolean Space (Area Requirements) / AVF_IncludeAsGFA PDF p.373

Deliberately not normalised — published spellings preserved verbatim because both sources agree: AI-DioxinsAndFurans (hyphen, Pollution Control), BarrierFreeAccessbility (Lift), and the Space (Area Requirements) example values Assisted Living Facilitys, Service Appartment II, Electrial Substation, plus the duplicated Mechanical Ventilation in Space (Usage) VentilationMode.

Footnote markers stripped from property names

Markers are removed from the stored property; the original cell is kept in property_raw. The footnote text itself has no corpus field, so it is recorded here:

Component Properties Footnote published under the table
Lift BarrierFreeAccessbility, ClearDepth, ClearHeight, ClearWidth, FireFightingLift, LiftType (^) "^Note: Properties to be provided for Ifc SubType = LIFT only" — 6 of Lift's 8 properties apply only to LIFT, not CARLIFT.
Refuse Chute / Recyclables Chute NominalCapacity (^) "^ Note: NominalCapacity for Compactor refers to the volume of waste before compaction. For example, if the capacity of the equipment if 6m3, the NominalCapacity is 18m3 due to compaction ratio of 1:3."
Road LoadingCapacity (^) table note: "* Note: PARKINGACCESSWAY refers to LTA's accessway to parking place"
Site Boundary BroadLandUse, VacantLand (*) marker only; the page's note is the IFC SubType = SITEBOUNDARY only scope text

A * on a value (e.g. Input Limitation = No*) is the page's "parameter is populated from the dimensions of BIM elements modelled" note. It appears under Column, Slab, Wall, Beam, Borehole, Footing / Pilecap, Pile and Household Shelter and does not change the parsed value.

Cross-validation against the CoP v3.1 PDF

scratchpad/validate_against_cop.py. Page offset verified empirically before use: corpus page_range is Section-4-relative and PDF page = value + 246 — 722 of 723 parsed properties land inside their own page range at +246, while the next-best offset scores 0.66.

Value layer

resolve_enums.py alone could not re-apply 46 value lists that the migrated corpus carried inline, because they had never been declared in _meta/enum_resolutions.json. 44 were re-declared there verbatim — 43 sourced from _reference/structural_enums.json (each asserted equal to its enum group before writing) and 1 = Site Boundary BroadLandUse (32 values, CoP p.349). The existing resolver now reproduces them. The remaining 2 are intentional, not losses:

Correction — Roof ConstructionMethod keeps its vocabulary

An exhaustive old-vs-new audit of every value list in the corpus found one property that had lost its vocabulary outright: Roof ConstructionMethod carried the 7 structural-enum values as allowed_values before, and briefly carried nothing after.

Roof is the same structural case as System SystemType — the live page and CoP p.339 both mark Input Limitation No while the example still reads "Refer to list*" — but the two were handled inconsistently: System kept its vocabulary as a soft reference_values, Roof kept none. Per the project's own calibration (official vocabulary on a free-input parameter is retained as a suggestion, not a constraint), _meta/enum_resolutions.json now carries a reference_resolutions entry for Roof ConstructionMethod with the 7 values copied verbatim from _reference/structural_enums.json and asserted equal to what corpus v3.1 carried. All three Roof bindings now expose them as reference_values.

The audit is now clean: 0 properties lost a vocabulary, and exactly one value-layer change remains corpus-wide — Roof ConstructionMethod allowed:7reference:7, which is this intended re-calibration.

A stale entry was also removed: System / SystemType still appeared in the hard resolved list alongside its documented soft entry. It was inert only because resolve_enums.py gates the hard branch on input_limited; had a later recrawl flipped that flag, the parameter would have silently regained a hard constraint against intent.

Input-limited parameters still awaiting a resolution

Component Property Status
Road LoadingCapacity New in this regeneration — no resolution entry yet. Live examples: 24, 30, 50 (tonnes).
Parking Lot VentilationMode Pre-existing pending (enum_resolutions.jsonpending).
Parking Lot SpaceName Pre-existing pending.
Wall ArrangementType Pre-existing pending.

The other 11 new properties (the DA1- / WorkingLoad_ / MinEmbedment* block, NominalCapacity, CompactionRatio) are free-input on both sources and need no resolution.

Component README.md / notes.md regenerated

Both files are shipped raw to dist/ and inlined into llms-full.txt, so after the corpus regeneration they contradicted the very data they describe — arc/lift/README.md and the llms-full.txt copy of it both still read "Parameters | 2" against a parameters.json holding 8. They are now generated by scripts/generate_component_docs.py, which runs after resolve_enums.py (notes cite allowed_values) and asserts, per component, that the stated counts and the parameter-table row count match parameters.json — a contradiction now fails the run instead of shipping.

75 files regenerated across 47 components: 45 README.md + 30 notes.md. 72 of them belong to the 44 components whose parameters.json this regeneration changed. The other three are the settled rule decisions below.

Editorial prose promoted to data. Four documents held hand-written passages. They now live verbatim in _meta/editorial_notes.json and are emitted from there, so the generator holds no per-file exceptions and "the corpus is generated, never hand-edited" stays literally true. All four were verified byte-identical to the originals after regeneration:

Document Passage
arc/space/README.md the parenthetical in the no-bindings sentence
arc/space/notes.md the ## Notes section
arc/space_area_requirements/notes.md ## Property prefix groups (IFC-SG Glossary), incl. the mandatory/key subsections
arc/space_usage/notes.md the - Related: provenance sub-bullet and ## Glossary page split (verified 2026-07-10)

Corrections made while regenerating