About capability parity¶
Two libraries now answer to one name. That is the point of the unification, and
it is also its sharpest risk. Once both are called idfkit, a reader who finds
something in Python and not in JavaScript has no way to tell whether they have
met a deliberate boundary or a defect, and the reasonable assumption is defect.
The parity ledger removes the guesswork. Every public capability of either library appears on it exactly once, with its availability in both languages, and every absence is described rather than left to be discovered. A written-down absence is documentation. An undocumented one is a bug report waiting to be filed against behaviour that is working as designed.
This page is the reader-facing view of that ledger. Everything below the
generated marker comes from governance/parity.toml in the
conformance repository, read at
the immutable governance tag this release pins, so the page you are reading and
the file both libraries' CI gates read cannot disagree. For the vocabulary side
of the same governance, which name each concept carries in each language, see
the naming map.
Three states, not two¶
A capability is complete, partial, or absent in each language.
The middle state is the one that earns its keep. A capability that exists in
both languages and behaves differently is neither present nor absent, and
calling it present is precisely the lie this ledger exists to prevent. Anything
recorded as partial carries a description of what differs, because partial
with nothing said about it is indistinguishable from complete.
Two kinds of absence¶
An absence alone tells you almost nothing. What you need to know is whether to wait or to plan around it, so every absent side is recorded as one of two kinds.
- Not yet. The second language could have this and does not have it today. The entry names the issue tracking the port. Expect this to change.
- Never. The capability is permanently single-language, and the entry says
why. A permanent record is a stronger claim than a temporary one, so moving a
capability out of
nevertakes a constitutional amendment rather than an edit to the ledger.
On the page, a temporary gap is an amber notice naming a tracking issue and a
permanent one is a plain notice naming a reason. In the table at the top, the
same distinction reads as absent (not yet) against absent (never).
One entry per mechanism¶
Where the two libraries do something recognisably similar by fundamentally different means, that is two capabilities and not one.
Simulation is the case that forces the rule. Python drives a locally installed
EnergyPlus through a subprocess; JavaScript runs a WebAssembly build in the
browser. Recorded as a single simulation entry, whichever language the entry
was written from would make the other read as deficient, and every page
describing it would print a gap notice for a capability that is not missing.
Visual output splits for the same reason: a static vector image drawn once is
not a real-time rendered scene.
So local-simulation and
browser-simulation appear separately, as do
svg-visualisation and
scene-rendering. Neither pair is ever collapsed. When you
read one of them as Python-only or JavaScript-only, its partner entry is where
the other language's answer lives.
The ids are load-bearing¶
Each capability's ledger id is a permanent public identifier, and it does three
jobs at once. It is the anchor on this page, so a link to #local-simulation
keeps resolving. It is the argument the parity() macro takes on every page
that describes the capability, which is how availability reaches the point of
use instead of living only here. And it is what the gates in both libraries
match their exported surface against.
Ids get added and deprecated. They do not get renamed.
Generated from
governance/parity.toml
at governance-2026.22, the governance tag this release pins. Correct the ledger and
regenerate; a correction made on this page would be overwritten, and it would never
reach either library's CI gate.
Every capability at a glance¶
35 capabilities, counted by availability and then listed in full. Follow a capability to read what differs where the two libraries differ, and whether an absence is temporary or permanent.
| Availability | Python | JavaScript |
|---|---|---|
| complete | 31 | 16 |
| partial | 1 | 3 |
| absent, not yet | 0 | 11 |
| absent, never | 3 | 5 |
Tier 1: the shared core¶
Both libraries carry these, and both are expected to keep carrying them. A difference you meet inside this tier is either stated below or a bug, and there is no third possibility.
Reading IDF and epJSON¶
Python complete · JavaScript complete · Tier 1 · ledger id parse
Vocabulary this capability owns in the naming register
- parse IDF from a string
- parse epJSON from a string
- read IDF from disk
- read epJSON from disk
- the parse error type
- detect a document version
- an epJSON document value
- read IDF from disk, keeping diagnostics
- detect an epJSON document version
Writing IDF and epJSON¶
Python complete · JavaScript complete · Tier 1 · ledger id write
Vocabulary this capability owns in the naming register
- serialize IDF to a string
- serialize epJSON to a string
- write IDF to disk
- write epJSON to disk
- serialize a document to an epJSON value
- serialize one object
Documents, collections, and objects¶
Python complete · JavaScript complete · Tier 1 · ledger id document-model
Vocabulary this capability owns in the naming register
- the document class
- the collection class
- the object class
- an extensible group
- create a new document
- add an object
- remove an object
- rename an object
- every object of a type
- untyped collection access
- one object, or nothing
- one object, or an error
- collection to a sequence
- an object's type name
- version
Reference graph¶
Python complete · JavaScript complete · Tier 1 · ledger id references
Vocabulary this capability owns in the naming register
- the reference graph
- objects referencing a name
- a reference edge
Schema access and the version registry¶
Python complete · JavaScript complete · Tier 1 · ledger id schema-access
Vocabulary this capability owns in the naming register
- load a schema for a version
- the supported version list
- resolve a version string
- one version's schema
- the difference between two schema versions
- resolve the schema for a detected version
- the process-wide schema source
Model validation¶
Python complete · JavaScript complete · Tier 1 · ledger id validation
Vocabulary this capability owns in the naming register
- validate a document
- validate an object
- validation result
- validation finding
- validation severity
Describing an object type from the schema¶
Python complete · JavaScript complete · Tier 1 · ledger id introspection
Vocabulary this capability owns in the naming register
- describe an object type
- object description
- field description
Building EnergyPlus documentation URLs¶
Python complete · JavaScript complete · Tier 1 · ledger id documentation-urls
Vocabulary this capability owns in the naming register
- resolved documentation URL
- documentation URL for an object type
- I/O reference URL
- engineering reference URL
- documentation search URL
Static types generated from the schema¶
Python partial · JavaScript complete · Tier 1 · ledger id generated-object-types
What differs, and why
Coverage differs, and the difference is visible to anyone not on the newest release. The Python stub set is generated for one EnergyPlus version at a time and is currently shipped for 26.1.0 only, so a model loaded at 9.4 gets the 26.1 field names and choice lists from the editor. The TypeScript type maps are emitted per version and selected by the caller, who parameterises the document with the map for the version being read, so an older model is typed as that older model or is left untyped rather than mistyped.
Application differs too. Python's stubs apply implicitly to every document. TypeScript's are opt-in by construction: an unparameterised document stays untyped, which is what version-generic code needs.
Vocabulary this capability owns in the naming register
- generated object types
- a version type map
Diagnostics from an IDF parse¶
Python complete · JavaScript complete · Tier 1 · ledger id parse-diagnostics
Vocabulary this capability owns in the naming register
- a parse diagnostic
- diagnostics from a parse
- recoverable diagnostics from a parse
- a document and its parse findings
The weather station index¶
Python complete · JavaScript partial · Tier 1 · ledger id weather-index
What differs, and why
Installation differs, and the difference is deliberate. pip install idfkit installs weather and
its station index unconditionally, because Python extras gate dependencies rather than files, so a
Python reader has weather whether or not they wanted it. npm install @idfkit/idfkit installs neither:
weather is an opt-in peer there, added with npm install @idfkit/weather. Both libraries ship
their own index once installed and neither retrieves one to get started (FR-043, FR-075,
research R11). A JavaScript reader who follows a weather page without installing that package
gets a resolution error, not a smaller feature, which is why the packaging is recorded here as a
stated difference rather than left as an undescribed detail.
Freshness handling differs. Python fires a throttled nudge from StationIndex.load(): at most once every 24 hours it probes the upstream KML files, warns when the bundled or cached index is behind, records the check under the cache directory, and can be turned off with IDFKIT_NO_WEATHER_UPDATE_CHECK. JavaScript has no such nudge and no timestamp to throttle against. It exposes checkForUpdates and refreshStationIndex for a caller who asks, and does nothing on its own, because the nudge is built on a writable cache directory and a browser-targeted package has none. The consequence for a reader is concrete: a stale index goes unmentioned in JavaScript until they check for themselves.
The installation difference above is the whole weather surface's, not the index's alone. It is
stated here because the index is where a reader meets it first, and weather-download and
weather-file-cache refer back to it rather than repeat it.
Vocabulary this capability owns in the naming register
- the station index
- search stations
- a weather station
- refresh the station index
- a text search result
- a proximity search result
- which field a text search matched
- the station wire record
- the upstream index base URL
- fetch a prebuilt station index
- parse a KML station index
- read station metadata from a download URL
- great-circle distance between two points
- the weather options-object types
- the station index wire form
- build an index from index data
- the upstream index file list
- the bundled station index
- check the station index for updates
Retrieving weather and design-day files¶
Python complete · JavaScript partial · Tier 1 · ledger id weather-download
What differs, and why
Both libraries retrieve a station's ZIP archive from climate.onebuilding.org and unpack the EPW,
DDY, and STAT members out of it. What they hand back differs, and it differs in the first line a
reader writes. Python's WeatherDownloader.download returns a WeatherFiles whose epw, ddy, stat, and
zip_path are Path objects, because the files are on disk by the time it returns and a path is what
EnergyPlus is given. TypeScript's fetchWeatherFiles returns a WeatherFiles whose epw, ddy, and stat
are the file TEXT, alongside a members map holding every archive member as bytes, because a browser
has no disk and the text is what @idfkit/engine takes. Same name, different values, which the
naming register records under the retrieved weather files: code written from one language's
documentation is wrong at runtime against the other rather than merely awkward.
Three things exist on one side only, and each follows from that one fact rather than from a gap. Python's download(station, only={".epw"}) extracts a chosen subset and returns PartialWeatherFiles; selective extraction is a property of writing into a cache, and the JavaScript side decodes from memory whatever the archive held. TypeScript's FetchWeatherOptions carries fetch, rewriteUrl, and signal, because climate.onebuilding.org sends no Access-Control-Allow-Origin header and a page can reach it only through a proxy the caller supplies; Python runs under no same-origin policy and owns its own urllib requests. TypeScript splits retrieving from writing, so @idfkit/weather/node adds saveWeatherFiles and SavedWeatherFiles, where in Python the two are one operation.
Resolving a canonical EPW filename differs in one argument. Python's index parameter is optional and defaults to the bundled index, because it can always find one on disk. TypeScript's is required, because the caller had to obtain an index already and the function has nowhere to load one from.
typescript = "partial" records the missing selective extraction and nothing else. It is not a
verdict on the on-disk cache: that is weather-file-cache, which is permanently absent by decision
rather than missing.
Vocabulary this capability owns in the naming register
- download a weather file
- download a station's EPW file
- download an EPW file by filename
- download a station's archive
- the retrieved weather files
- read a ZIP archive
- the injectable fetch
- write weather files to disk
- the written weather file paths
Declaring the conformance level a release passes¶
Python complete · JavaScript complete · Tier 1 · ledger id conformance-declaration
Vocabulary this capability owns in the naming register
- declared conformance level
Geocoding a place name¶
Python complete · JavaScript complete · Tier 1 · ledger id geocoding
Vocabulary this capability owns in the naming register
- geocode a place name
- detect the current location
- the geocoding rate limiter
- a geocoding failure
Tier 2: portable, not ported yet¶
Capabilities the second language could have and does not have today. Every entry carries a tracking issue, because recording any of them as permanent would claim more than the code supports.
Formatting-preserving round-trip¶
Python complete · JavaScript complete · Tier 2 · ledger id lossless-round-trip
Vocabulary this capability owns in the naming register
- preserve formatting on a round-trip
- the original source text
- label a field the author left without a comment
- the objects a preserving write will rewrite
- the retained source structure
Reading geometry from a model¶
Python complete · JavaScript partial · Tier 2 · ledger id geometry-extraction
What differs, and why
TypeScript carries the vector, the polygon and the resolved scene. getScene places every detailed
surface a model states and reports what it could not place and what it did not attempt, so a reader
who wants a model's geometry has it in both languages.
What it does not carry is the per-object calculation surface Python spells as free functions over an
IDF object: a zone's origin and its rotation, a surface's area, tilt and azimuth read from the
object rather than from a polygon, a zone's floor area, ceiling area, height and volume, and the
two-dimensional polygon predicates. Python's translate_to_world has no counterpart either, and
will not have the same one: it resolves by mutating the document, where getScene resolves into a
value and leaves the model alone.
Vocabulary this capability owns in the naming register
- three-dimensional vector
- three-dimensional polygon
- read a surface's coordinates
- a zone's origin
- a zone's rotation
- transform relative coordinates to world coordinates
- a surface's area
- a surface's tilt
- a surface's azimuth
- a zone's floor area
- a zone's volume
- a model's resolved geometry
- the resolved scene
- a resolved surface
- the rules resolution applied
- the scene's extent
- a surface that could not be resolved
- a geometry type the reader did not attempt
Building and transforming geometry¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id geometry-authoring
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#14.
Vocabulary this capability owns in the naming register
- set the window to wall ratio
- rotate a building
- translate a building
- scale a building
- add a shading block
- set default constructions
Intersecting and matching surfaces¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id surface-matching
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#15.
Vocabulary this capability owns in the naming register
- intersect and match surfaces
- a surface match report
Generating zoned blocks from a footprint¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id zoning
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#16.
Vocabulary this capability owns in the naming register
- create a zoned block
- a zone footprint
- a zoning scheme
- link blocks
Construction thermal properties¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id thermal-properties
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#17.
Vocabulary this capability owns in the naming register
- construction thermal properties
- a construction's U-value
- a construction's R-value
- a construction's solar heat gain coefficient
- a construction's layers
Schedule evaluation¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id schedules
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#18.
Vocabulary this capability owns in the naming register
- evaluate a schedule
- a schedule's values for a year
- a schedule as a series
- create a constant schedule
- create a compact schedule
- extract special days
- the holidays in a model
Reading the output variable dictionary and selecting variables¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id output-variable-selection
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#19.
Vocabulary this capability owns in the naming register
- read the output variable dictionary
- an output variable
- an output meter
- select output variables for a run
Design days and ASHRAE sizing conditions¶
Python complete · JavaScript absent (not yet) · Tier 2 · ledger id design-day-sizing
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#20.
Vocabulary this capability owns in the naming register
- apply ASHRAE sizing conditions
- the design day manager
Reading an EPW weather file¶
Python complete · JavaScript complete · Tier 2 · ledger id weather-file-reading
Vocabulary this capability owns in the naming register
- read a weather file from text
- read a weather file from a source
- the weather file a reader returns
- monthly aggregates of a weather file
Tier 3: tooling and Node-bound capabilities¶
Absent from JavaScript today and not permanently so. Each one is reachable in Node, which is why none of them is recorded as permanent.
Forward-migrating a model between EnergyPlus versions¶
Python complete · JavaScript absent (not yet) · Tier 3 · ledger id version-migration
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#21.
Vocabulary this capability owns in the naming register
- migrate a model to a newer version
- a migration report
- a migration step
Command-line interface¶
Python complete · JavaScript absent (not yet) · Tier 3 · ledger id command-line
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#22.
Vocabulary this capability owns in the naming register
- the command line entry point
Checking source against another EnergyPlus version's schema¶
Python complete · JavaScript absent (not yet) · Tier 3 · ledger id schema-compatibility-check
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#23.
Vocabulary this capability owns in the naming register
- check source compatibility with a version
- diff two schemas
- a compatibility diagnostic
Plotting simulation results¶
Python complete · JavaScript absent (not yet) · Tier 3 · ledger id result-plotting
Not in JavaScript yet
A temporary gap, not a boundary. The port is tracked in idfkit-js#24.
Vocabulary this capability owns in the naming register
- plot an energy balance
- plot a temperature profile
- plot comfort hours
- a plotting backend
Permanently single-language¶
Pairs of capabilities that were never one capability. Neither library is missing anything here, which is why these entries sit apart from the gaps above: each pair is two different mechanisms serving two different runtimes.
Running a locally installed EnergyPlus and reading its results¶
Python complete · JavaScript absent (never) · Permanently single-language · ledger id local-simulation
Python only, permanently
Requires an EnergyPlus installation on the machine and a subprocess to drive it, then reads the
eplusout files that run leaves on disk. Neither the installation nor the subprocess is available in
a browser, which is the runtime the JavaScript library targets. JavaScript reaches EnergyPlus by
the other mechanism instead: see browser-simulation, which is not a workaround for this entry but
a different capability.
Vocabulary this capability owns in the naming register
- the local simulation surface
Running EnergyPlus in the browser¶
Python absent (never) · JavaScript complete · Permanently single-language · ledger id browser-simulation
JavaScript only, permanently
Delivered by @idfkit/engine, installed separately and deliberately not part of the shared install name: not a subpath, not a dependency, not an optional peer (research R19). The WebAssembly build exists to reach a runtime Python does not target, so Python has no counterpart and is not getting one. @idfkit/engine-assets is roughly 51 MB and versions on the EnergyPlus release it carries, while the loader versions on its own API, which is a second reason the facade does not carry it.
This is not a gap in Python. Python runs EnergyPlus by the other mechanism: see
local-simulation.
Rendering a model to a vector image¶
Python complete · JavaScript absent (never) · Permanently single-language · ledger id svg-visualisation
Python only, permanently
A Python-side capability with no JavaScript counterpart and no plan for one. The output is a static
vector image produced for a notebook, a report, or a file on disk, which is a Python workflow. A
JavaScript caller wanting to look at a model is already in a runtime that draws, and is served by
the other mechanism: see scene-rendering. Recording the two as one visualization entry would
make each language read as missing something the other has, which is exactly the failure FR-078
and FR-068 forbid.
Vocabulary this capability owns in the naming register
- the vector image surface
Rendering a three-dimensional scene¶
Python absent (never) · JavaScript absent (never) · Permanently single-language · ledger id scene-rendering
Permanently absent
To be delivered by @idfkit/viewer, installed by its own name like the simulation engine, and NOT reachable through the shared install name: no subpath, no dependency, no export-map entry, and no reserved name in the naming register. A reader would reach it by installing it explicitly, and the documentation says so rather than implying the facade carries it.
@idfkit/viewer DOES NOT EXIST. This row read typescript = "complete" until the change that landed
geometry extraction, and no such package is in idfkit-js/packages, published or unpublished. The
distinction matters more here than the word "unreachable" carries on its own: a reader meeting only
this row would otherwise conclude that a renderer is a npm install away, and a specification
waiting on one would be waiting on a claim rather than on a package. Nothing in feature 009 depends
on this row's value, and nothing waiting on a renderer may.
The single absence_kind covers two absences of different character, and the file has one field for
it. Python's absence is terminal: a real-time interactive scene needs a rendering context that a
Python process does not have, and Python's static vector output is a different mechanism serving a
different workflow, svg-visualisation. Neither is a gap in the other. TypeScript's absence is
merely present-tense, and the never this row carries is the one its tier already states: this
capability is not part of the shared surface in either language, whoever builds it and whenever.
eppy compatibility surface¶
Python complete · JavaScript absent (never) · Permanently single-language · ledger id eppy-compatibility
Python only, permanently
A Python ecosystem concern with no JavaScript counterpart. These methods exist solely so that code written against eppy keeps working after a move to idfkit; every one of them names an idfkit alternative in its own docstring. There is no eppy in JavaScript, so there is nothing to be compatible with. Listed explicitly so it is never ported by mistake.
Vocabulary this capability owns in the naming register
- eppy compatibility surface
Caching retrieved weather files on disk¶
Python complete · JavaScript absent (never) · Permanently single-language · ledger id weather-file-cache
Python only, permanently
FR-031, stated as a decision rather than a backlog item: the on-disk cache for retrieved weather and
design-day files is Python's, and JavaScript MUST NOT grow one. Moving this entry out of never
needs a constitutional amendment, not an edit here.
What Python has. WeatherDownloader owns a cache directory, per platform by default and moved with cache_dir or IDFKIT_CACHE_DIR. download() looks in it first and reaches the network only on a miss or past max_age, so the second call for a station costs nothing; clear_cache empties it. Everything downstream follows: the record it returns is paths rather than text, zip_path addresses the archive it kept, and selective extraction through only= is meaningful because a suffix skipped this time may already be sitting there from an earlier call.
Why JavaScript will not have one. @idfkit/weather targets a browser, a worker, and an edge runtime as well as Node. Two of those have no directory it could own, and picking one in Node would make the package behave differently depending on where it runs, which is the one thing a portable package must not do. So it fetches every time and returns text the caller keeps. saveWeatherFiles in @idfkit/weather/node writes a retrieved bundle where the caller says, and that is a caller-directed write rather than a cache: it consults nothing on the way in and remembers nothing on the way out.
This is not a gap in JavaScript, and it is why several one-sided names exist on each side. The
station index is shipped, not cached, in both languages (FR-043, FR-075), so nothing here changes
how either library finds a station: that is weather-index. Retrieval itself is present in both
languages: that is weather-download.
Vocabulary this capability owns in the naming register
- the weather file cache
Language service for IDF text¶
Python absent (never) · JavaScript complete · Permanently single-language · ledger id idf-language-service
JavaScript only, permanently
Deliberately second-language-only. The answers are computed from byte offsets into the source text, and a second implementation of that arithmetic is the drift surface the corpus is least able to police: it compares findings on (code, line, typeName) and never on a column, so two implementations could disagree about a position for a long time without any gate noticing.
The editor extension serves both file kinds without a port. Its existing server, written in Python, continues to serve Python source; a second server written in JavaScript serves IDF text and wraps this capability. One implementation, two servers.
What this costs a reader: getting these answers requires a JavaScript runtime. pip install idfkit
alone does not provide them.
Vocabulary this capability owns in the naming register
- scan IDF text
- classify IDF text
- line and column at an offset
- offset at a line and column
- a source region
- a line and column
- a syntax token
- a syntax token kind
- the syntax layer
- a written statement
- schema prose pool
- cursor context
- completions at an offset
- explanation at an offset
- declaration at an offset
- position findings
- position findings already in hand
- the cursor context record
- a positioned finding
- a completion offer
- an explanation
- a declaration site
- the completion options
- a completion result
- an explanation result
- a declaration result