ifc-properties 0.2.0

Property sets, quantities, and unit resolution. No geometry.
Documentation
# ifc-properties implementation plan

Status: implemented; every task in the work queue is complete.
Last updated: 2026-08-19

This is task state, not ambient context. Follow `AGENTS.md`; claim one task ID,
record blockers/decisions under it, and check it off only with evidence.

## Established boundary

Borrowed property, quantity, unit, template, and standard-library projections plus model authoring ports.

## Planned file map

These paths are compiled private scaffold modules. Implement inside the named
owner and expose a public symbol only through an intentional parent re-export.

- `src/pset/set.rs`: IfcPropertySet and relationships
- `src/pset/scalar.rs`: single/bounded/list/enumerated values
- `src/pset/table.rs`: table values and interpolation metadata
- `src/pset/reference.rs`: object/reference properties
- `src/pset/complex.rs`: nested complex properties
- `src/quantity/set.rs`: IfcElementQuantity
- `src/quantity/simple.rs`: length/area/volume/count/time/weight
- `src/quantity/complex.rs`: nested physical complex quantities
- `src/quantity/edit.rs`: transactional authored quantity updates
- `src/quantity/validation.rs`: units/dimensions/formula consistency
- `src/unit/assignment.rs`: project unit context
- `src/unit/si.rs`: SI prefixes/dimensions
- `src/unit/conversion.rs`: conversion-based units
- `src/unit/derived.rs`: derived dimensions/elements
- `src/template/property_set.rs`: set templates
- `src/template/property.rs`: property templates
- `src/query/assignment.rs`: object/type set assignment

- `src/pset/aggregate.rs`: compiled private scaffold; implementation owned by `src/pset/PLAN.md`
- `src/template/relationship.rs`: compiled private scaffold; implementation owned by `src/template/PLAN.md`
- `src/unit/monetary.rs`: compiled private scaffold; implementation owned by `src/unit/PLAN.md`

## Work queue

- [x] `PROP-PSET` - implement all property value families as borrowed views
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.
- [x] `PROP-QTY` - implement authored simple/complex quantity views
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.
- [x] `PROP-UNIT` - implement dimensional unit resolution shared by properties/quantities
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.
- [x] `PROP-TEMPLATE` - implement templates and applicability links
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.
- [x] `PROP-QUERY` - resolve occurrence/type property assignment with precedence made explicit
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.
- [x] `PROP-EDIT` - write/update quantities transactionally after MODEL-MUT
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.
- [x] `PROP-CHECK` - accept externally computed measurements and compare without depending on geometry
  - Evidence: focused unit/property/fixture tests, isolated build, and crate clippy.

## Completion log

Append concise entries as `TASK-ID - proof command/result - material decision`.
Do not paste long logs or move standing invariants out of `AGENTS.md`.

PROP-PSET - cargo test -p ifc-properties (19 passing) - IfcPropertyBoundedValue
  states UpperBoundValue at slot 2 and LowerBoundValue at slot 3; reading them
  in the intuitive order inverts every range and still looks plausible.
  CurveInterpolation is an ENUM (.LINEAR.), not text: a text reader silently
  reports "unstated".
PROP-QTY - cargo test -p ifc-properties (19 passing) - simple quantity values
  live at slot 3 because Unit occupies slot 2 on IfcPhysicalSimpleQuantity;
  IfcPhysicalComplexQuantity has no Unit so its contents start at slot 2. WR21
  (unit type matches quantity kind) and WR22 (value >= 0) are reported as
  anomalies: ifcopenshell.validate does not check them.
PROP-UNIT - cargo test -p ifc-properties (19 passing) - SI prefixes are carried
  as decimal EXPONENTS, not factors: MILLI is 1e-3 exactly as a decimal and
  f64 cannot hold it, so the caller applies one multiplication instead of a
  chain of roundings. IfcDerivedUnit is NOT an IfcNamedUnit and its slots start
  at 0, so it cannot share a reader with IfcSIUnit.
PROP-TEMPLATE - cargo test -p ifc-properties (19 passing) - templates describe
  what a set should contain and carry no values, so they are read separately
  from psets rather than as a set variant.
PROP-QUERY - cargo test -p ifc-properties (19 passing) - occurrence overrides
  type by set NAME; the shadowed type set is retained so a checker can explain
  why an effective value differs from the type default. IfcRelDefinesByType
  puts objects at slot 4 and the type at slot 5, the OPPOSITE roles to
  IfcRelDefinesByProperties' same two slot numbers.
PROP-CHECK - cargo test -p ifc-properties (19 passing) - comparison takes a
  caller-computed value and never opens geometry; unit differences are
  REPORTED rather than converted, because a silent mm/m conversion is how a
  1000x error passes a check.

PROP-EDIT - cargo test -p ifc-properties (25 passing) - helpers STAGE onto a
caller-owned `Transaction` and never commit, so a takeoff spanning many
elements lands atomically or not at all. Writes preserve the declared measure
type and refuse a non-quantity target before staging, so a rejected edit never
reaches the batch. `IfcCountMeasure` is written as an INTEGER per the schema.