Decision records
Each record states one decision, the blueprint sections it governs, the charter
§H fields (principles, level, evidence, revisit trigger, verification), and the
design-review finding that motivated it. A record is immutable once accepted:
it changes only by superseding it with a new record, or by a status edit and an
appended ## Status history line. docs/authoritative_design/blueprint.md is
the authoritative design; these records say why it reads the way it does.
Run just adr-new <slug> to start one and just adr-lint before opening the PR.
Open items that were deliberately deferred live in register.md.
| ID | Title | Status | Date | Blueprint | Revisit |
|---|---|---|---|---|---|
| ADR-0001 | Record decisions as ADRs with the charter §H fields | accepted | 2026-09-13 | §0.1 | A second maintainer joins, or two consecutive months pass in which ADRs are written after the change rather than before it |
| ADR-0002 | Name the project pse-arrow, prefix crates pse-, dual-license MIT OR Apache-2.0 | accepted | 2026-09-13 | §3.2, §26 | The PyPI name pse becomes available, or a crates.io name collision forces a different crate prefix at the phase-0 publish |
| ADR-0003 | Re-implement IDAES clean-room and pin parity to idaes-pse 2.12.0 | accepted | 2026-09-13 | §0.2, §3.1, §6.14, §25 | IDAES releases a version that changes an enumeration preserved by name in blueprint §6.14, or drops a CPython version the parity environment needs |
| ADR-0004 | Adopt D1: typed Arrow relations are the only model authority | accepted | 2026-09-13 | §D1, §4.1, §6 | A relation family needs content no typed schema can express and a JSON column is proposed for it a second time |
| ADR-0005 | Adopt D2: authors write primitive facts, passes derive everything else | accepted | 2026-09-13 | §D2, §14.1, §22.2 | A derived relation acquires an authored write path, or derivation rows exceed the §24.3 benchmark’s budget per equation |
| ADR-0006 | Adopt D3: one catalog holds CanonicalModel, CanonicalMathGraph and CanonicalMathProblem | accepted | 2026-09-13 | §D3, §5, §14.1 | A fourth artifact level is proposed, or a level needs identity, hashing or provenance conventions the other two do not share |
| ADR-0007 | Adopt D4: semantic IDs, artifact ordinals and content hashes are three different things | accepted | 2026-09-13 | §D4, §5.1, §5.2 | A relation needs an identity that is neither a semantic ID, an ordinal nor a content hash |
| ADR-0008 | Adopt D5: every quantity carries a quantity_type_id, and pse.* extension types are registered | superseded | 2026-09-13 | §D5, §4.4, §8 | A quantity-bearing column appears without a quantity_type_id, or the engine’s extension-type registry stops validating storage types at planning time |
| ADR-0009 | Adopt D6: the math IR is relations, and DataFusion Expr computes over it | accepted | 2026-09-13 | §D6, §7.1, §7.2 | A backend requires an operator the IR cannot express without scalarizing earlier than §7.1 allows |
| ADR-0010 | Adopt D7: balance, costing and utility laws are templates expanded over a contributions substrate | accepted | 2026-09-13 | §D7, §10.1, §10.2 | A law family cannot be expressed over contributions and needs a compiler rule of its own |
| ADR-0011 | Adopt D8: a pass computes the property closure; inspection never constructs physics | superseded | 2026-09-13 | §D8, §9.6 | A property provider needs to be selected at evaluation time rather than at P6, for example because a method’s applicability depends on a solved value |
| ADR-0012 | Adopt D9: every constitutive computation is a KernelSpec with generated bindings | superseded | 2026-09-13 | §D9, §18.5, §21.4 | A kernel needs a binding form the six generated ones cannot express, or a third-party compiled kernel package is admitted (§22, ADR-0037) |
| ADR-0013 | Adopt D10: DataFusion has four roles and never runs inside a Newton iteration | accepted | 2026-09-13 | §D10, §5.4, §14.2 | A fifth role is proposed for DataFusion, or Exact pushdown stops holding for the key shapes §5.4 depends on |
| ADR-0014 | Adopt D11: execution layouts are compiled artifacts and zero-copy is a preference | superseded | 2026-09-13 | §D11, §18.2, §15.4 | A borrow precondition is relaxed, or a measurement shows the copy path dominates an evaluation benchmark |
| ADR-0015 | Adopt D12: one generated Pyomo adapter, no IDAES class hierarchy | accepted | 2026-09-13 | §D12, §21.1, §21.2 | A Pyomo-ecosystem tool needs a per-node API the coarse-grained bundle cannot serve, or a native equivalent makes the adapter redundant |
| ADR-0016 | Adopt D13: initialization, homotopy and fix-then-release are immutable case overlays | accepted | 2026-09-13 | §D13, §19.1, §17.1 | An operation genuinely needs to change structure mid-run, for example a native GDP lowering that adds equations during a solve |
| ADR-0017 | Adopt D14: memoize on declared inputs, the pass version and the engine profile | superseded | 2026-09-13 | §D14, §14.3, §14.4 | A measurement shows sub-pass granularity finer than per-instance is needed (the same trigger as ADR-0020), or an undeclared read is found in a pass |
| ADR-0018 | Pin both families with =, commit the lockfile, run cargo deny, and set MSRV to the pinned toolchain | accepted | 2026-09-13 | §3.1, §3.2, §3.3 | A DataFusion or Arrow release requires a Rust version above the pinned toolchain, or a pre-1.0 crate raises its declared floor above ours |
| ADR-0019 | Record the DataFusion plan fingerprint as evidence; keep it out of the memo key | superseded | 2026-09-13 | §14.2, §14.3, §20.2 | datafusion-proto gains a documented byte-stability guarantee, or the two-process byte-equality test fails after a DataFusion upgrade |
| ADR-0020 | Defer salsa; the artifact-hash memo is the only memoization mechanism | superseded | 2026-09-13 | §14.3 | A measurement shows sub-pass granularity finer than per-instance is needed inside P7 |
| ADR-0021 | Load YAML with serde-saphyr; serde_yaml is banned | accepted | 2026-09-13 | §3.1, §3.3 | serde-saphyr becomes unmaintained, or a document form the authoring language needs is not expressible through it |
| ADR-0022 | Write every kernel against num-dual 0.15; FeOs stays conditional | accepted | 2026-09-13 | §3.1, §3.3, §9.8 | feos-core releases a version that depends on num-dual 0.15 (checked monthly with cargo info) |
| ADR-0023 | Give pse-ids sole ownership of hashing, with derive_key contexts and pse.canon.v1 | superseded | 2026-09-13 | §5.1, §5.3, §20.1 | A canonicalization constant changes (alignment, metadata version, legacy flag, compression), which is a new pse.canon.vN and invalidates every stored hash |
| ADR-0024 | Cross the Python boundary with pyo3-arrow and the PyCapsule stream protocol | accepted | 2026-09-13 | §3.1, §21.1 | arrow-pyarrow drops its build-time interpreter requirement, or pyo3-arrow stops tracking the pinned arrow family |
| ADR-0025 | Defer LogicalPlan::Extension rule nodes; derivations already answer the attribution question | accepted | 2026-09-13 | §14.2 | The §22.4 diff report or agent tooling needs per-rule attribution inside a plan rendering |
| ADR-0026 | Drop uom and arrow-flight; pint validates what the adapter emits and never defines a quantity | accepted | 2026-09-13 | §3.1, §3.3, §21.2 | A kernel genuinely needs compile-time unit typing, or a remote Arrow transport becomes a requirement |
| ADR-0027 | Define the commit contract as P2-level validity; compilability is gated separately | accepted | 2026-09-13 | §22.2, §14.5 | A consumer is found treating a committed revision as compilable, or P2 grows a check expensive enough to make commits slow |
| ADR-0028 | Build Ipopt 3.14.20 with MUMPS and ASL from pinned sources in a container; probe HSL at runtime | accepted | 2026-09-13 | §18.3, §6.13, §26 | A platform’s recipe cannot be reproduced (macOS or Windows at phase 1b), or a wheel needs the native backend and the linkage question comes due |
| ADR-0029 | Give every session a FairSpillPool sized from a declared memory limit | superseded | 2026-09-13 | §14.3 | A workload’s spill rate makes the declared limit the binding constraint on compile time, or a deployment needs a different pool discipline |
| ADR-0030 | Canonicalize NaN and preserve -0.0 on the hashing path; never use Float64 as a distinct or join key | accepted | 2026-09-13 | §5.3, §14.2, §19.2 | DataFusion changes its grouping or join equality for floats, or a rule genuinely needs a float-valued key |
| ADR-0031 | Commit every generated source and diff it in CI; never generate in build.rs | accepted | 2026-09-13 | §4.2, §18.3, §21.5 | Generation becomes slow enough that an explicit step is a friction point, or a generated artifact needs a host-specific input a checkout cannot supply |
| ADR-0032 | Fetch external reading copies into a gitignored external/ rather than vendoring them | accepted | 2026-09-13 | §3.1, §0.2 | A CI job needs an external tree that a network fetch cannot supply reproducibly |
| ADR-0033 | Keep one blueprint file, revised in git, with section numbers as stable citation targets | accepted | 2026-09-13 | §0.1 | A revision needs to remove a section entirely, or the file grows past the point where one document is readable |
| ADR-0034 | Govern with a single maintainer, PR-only squash merges onto a linear signed main | accepted | 2026-09-13 | §0.1 | A second maintainer joins, which changes the approval count, the bypass policy and the project board question |
| ADR-0035 | Update dependencies with Dependabot, grouped by the pinned families | accepted | 2026-09-13 | §3.1 | Dependabot cannot express a grouping the family rule needs, in which case Renovate is the declared fallback (register row R-13) |
| ADR-0036 | Publish the documentation as an mdBook on GitHub Pages | accepted | 2026-09-13 | §0.1, §0.3 | The Python package grows a public surface that needs generated API documentation (register row R-14), or the book outgrows a single SUMMARY.md |
| ADR-0037 | Defer datafusion-tracing, instrumented-object-store, datafusion-ffi and egglog, each with a trigger | accepted | 2026-09-13 | §3.3, §14.2, §23.1, §22.1 | Any one of the four triggers fires: a datafusion-tracing release matching the pinned engine; an instrumented-object-store release matching it; a third-party compiled kernel package under §22; a cross-process, cross-version egglog extraction-determinism test passing |
| ADR-0038 | Add pse-ipopt-sys, pse-buildinfo, xtask, benches and five test crates to the workspace layout | accepted | 2026-09-13 | §3.2, §24.1, §24.3, §18.3 | A sixth test family is needed, or xtask grows logic that belongs in a shipped crate |
| ADR-0039 | Enforce semantic admission and complete physical quantity operations | accepted | 2026-09-13 | §D5, §4.1, §4.3, §4.4, §6.2, §8.3, §14.2 | A new quantity operation cannot be expressed by the registered composition policies, or a supported query bypasses generated admission. |
| ADR-0040 | Resolve property demand from normalized seeds and name immutable stage outputs | accepted | 2026-09-13 | §D8, §4.1, §6.11, §9.6, §14.1 | A selected method requires demand that cannot be declared over the finite normalized scope before realization. |
| ADR-0041 | Reuse complete stage inputs before introducing finer memoization | accepted | 2026-09-13 | §D14, §6.11, §14.3, §14.4 | Representative cold/warm measurements show whole-stage recomputation dominates total preparation cost and a complete finer dependency bundle passes differential checks. |
| ADR-0042 | Defer finer memoization until whole-stage measurements justify it | accepted | 2026-09-13 | §D14, §14.3, §24.3, §26 | A measured workload still needs finer dependency tracking after complete per-instance reuse is characterized. |
| ADR-0043 | Declare kernel outcomes and generate only supported bindings | accepted | 2026-09-13 | §D9, §6.11, §7.6, §18.5, §18.9, §21.4 | A current kernel needs an execution or outcome policy that the descriptor cannot express without backend-specific semantic invention. |
| ADR-0044 | Record plan encodings as noncanonical diagnostic evidence | accepted | 2026-09-13 | §4.3, §6.13, §14.2, §20.2 | A consumer demonstrates a need for canonical plan bytes, or upstream supplies a versioned byte-stability contract worth adopting. |
| ADR-0045 | Separate canonical logical content from encoded artifact integrity | accepted | 2026-09-13 | §5.1, §5.3, §20.1, §20.2, §20.4, §20.5 | Any canonical constant or normalization rule changes, or a measured workload exceeds the supported relation-size envelope. |
| ADR-0046 | Share runtime budgets and reserve platform allocations explicitly | accepted | 2026-09-13 | §14.3, §18.2, §18.8, §20.1, §24.3 | Measured pool/process peaks diverge materially or a supported consumer cannot reserve before allocation. |
| ADR-0047 | Preserve guarded numerical semantics and borrow through safe Arrow views | accepted | 2026-09-13 | §D6, §D11, §6.11, §7.3, §7.4, §18.2, §21.2 | A backend cannot express an adopted numerical policy or measured buffer-transfer cost justifies a new layout. |
| ADR-0048 | Use standard relational operators and explicit-schema readers | accepted | 2026-09-13 | §5.4, §6.10, §6.11, §14.2, §21.1, §24.1, §26 | A representative supported scan/import needs an operation outside the bounded algebra or measured optimization triggers in R-24/R-25 fire. |
| ADR-0049 | Pin serde_json and uuid as direct dependencies for the manifest codec and explicit identities | accepted | 2026-09-13 | §3.1, §5.1, §20.2 | a hash frame, a relation payload or a second document codec is routed through serde_json, or a pass other than the authoring assign-ids command mints a UUID |
| ADR-0050 | Freeze the identity framing constants, canonical frames and the phase-1 relation envelope | accepted | 2026-09-13 | §4.3, §5.1, §5.3, §14.3 | any constant listed here has to change — that is a new version string and a superseding record, never an edit |
| ADR-0051 | Extend the generated-tree list and make codegen –check a regeneration-equivalence check | accepted | 2026-09-13 | §4.2, §24.1 | the Ipopt bindgen arm starts running its generator in CI’s solver container, or the hand-written Rust manifest struct is generated (register row R-27) |