Skip to content

Release v2.0.0#289

Merged
K20shores merged 10 commits into
releasefrom
main
Jun 30, 2026
Merged

Release v2.0.0#289
K20shores merged 10 commits into
releasefrom
main

Conversation

@K20shores

Copy link
Copy Markdown
Collaborator

No description provided.

K20shores and others added 10 commits May 14, 2026 14:17
Introduces a CMake option `MECH_CONFIG_USE_FMT` (default OFF) to use the
{fmt} library instead of `std::format`. A shim header (`format_compat.hpp`)
aliases fmt or std into `mc_fmt` so call sites are unchanged. Packaging
conditionally installs fmt when built from source and emits
`find_dependency(fmt)` when found via the system. fmt headers are exposed
with PUBLIC linkage since they appear in the public `format_compat.hpp`
header. CI tests both `MECH_CONFIG_USE_FMT=OFF` and `ON` via a matrix
dimension across Mac, Ubuntu, and Windows workflows.

Adds a `MECH_CONFIG_COMPILE_WARNING_AS_ERROR` option (default OFF) that
passes `/WX` on MSVC and `-Werror` elsewhere. Enabled in all CI workflows
and Dockerfiles. Fixes the clang-cl warnings this exposed: removes unused
`status` variables in `v0/parser.cpp` and `v1/mechanism_parsers.cpp`,
drops unused `phase_optional` return values in the development model
validators, and removes redundant `std::move()` on temporaries returned
by `GetComments()` (pessimizing-move).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: GitHub Actions <actions@github.com>
Setting BUILD_SHARED_LIBS globally with FORCE causes all subsequent
FetchContent dependencies (e.g. TUV-x) to also build as shared, which
breaks their builds if their objects lack -fPIC.

Instead, set POSITION_INDEPENDENT_CODE on the fmt target directly after
fetch so fmt's static library is PIC-safe when linked into a shared
mechanism_configuration, without side effects on other dependencies.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…rivate headers) and support in-code mechanisms (#276)

* refactoring v1

* v0 refactor

* development

* Accept `name` or `species name` for reaction components (v1 alias)

Add an `exactly_one_of` group to ValidateSchema so a schema can require
exactly one of a set of keys, then use it to let reaction components
reference their species with either the canonical `name` or the legacy
`species name` alias (used by v1 config files): exactly one, error if both
or neither.

- ValidateSchema: defaulted `exactly_one_of` param. None present ->
  RequiredKeyNotFound; more than one -> MutuallyExclusiveOption. Group
  members count as allowed keys (never flagged invalid).
- validation.hpp: add `species_name = "species name"`.
- ValidateReactantsOrProducts requires exactly one of {name, species name}.
- New GetReactionComponentName() helper; route the component-name reads
  across all reaction validators and the parser through it.
- Tests + fixtures: AcceptsSpeciesNameAlias, RejectsBothNameAndSpeciesName.

Prep for consolidating development -> v1 (issue #269): keeps community v1
files (which use `species name`) parseable by the development engine.

Also adds planning docs: PLAN-consolidate-v1.md, TODO-name-species_name-alias.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Rename development engine to v1; delete the old combined v1 parser

Consolidate onto a single v1 engine (issue #269). The two-phase
Validate()/Parse() "development" engine becomes the canonical v1
implementation; the old combined v1 parser is removed.

- Delete old src/v1 combined parser (parser.cpp, mechanism_parsers.cpp,
  utils.cpp, reactions/*_parser.cpp) and its headers (parser.hpp,
  mechanism_parsers.hpp, reaction_parsers.hpp, validation.hpp, utils.hpp).
- git mv src/development/* -> src/v1/* and include/.../development/* ->
  include/.../v1/* (sources, headers, reactions/{parsers,validators} tree,
  CMakeLists), preserving history.
- Sweep namespace development -> v1 and mechanism_configuration/development/
  includes -> v1/. Old v1::validation (with its own species_name) is gone;
  the engine now resolves unqualified validation:: to the shared top-level
  namespace.
- Drop add_subdirectory(development) from src/CMakeLists.txt; remove the
  emptied development dirs and a dead, unreferenced reaction_types.hpp stub.

Library builds. Tests are not yet migrated (Stage 4): test/unit/v1 still
calls the old API and test/unit/development references the gone namespace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Wire top-level parse() dispatch (file + string)

Replace the throwing stub with a real version dispatch returning
std::expected<Mechanism, Errors>:
- directory input or a missing version field -> v0 parser
- major 0 -> v0, major 1 -> v1 engine (FileToYaml -> Validate -> Parse)
- unsupported versions -> InvalidVersion
Add parse(const std::string&) for in-memory v1+ documents.

Remove the dead f() and commented-out dispatch; re-enable the v0 include;
fix parse.hpp (drop the duplicated declaration, add the string overload).

validate(const Mechanism&) (the in-code path, Option 2) and test migration
are still pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Stage 4: unify v1 tests on the engine; add file-list config support

Engine / parse():
- Engine now accepts major version 1 (MAJOR_VERSION 1, not 2), since the
  renamed engine IS the v1 parser.
- Add v1::Parser::ResolveFileConfig(): ports the multi-file "{ files: [...] }"
  logic from PR #256 to the unified engine. species/phases/reactions sections
  may be split across files (v1.1+); they are merged into one inline node, then
  validated/parsed normally. Inline sections pass through unchanged. Structural
  checks (missing 'files' key, non-array/object section, minor-version >= 1)
  and file-load errors are reported with the config path prefix.
- parse() v1 case now goes ResolveFileConfig -> Validate -> Parse.
- Drop the parse(const std::string&) content overload: it hijacked
  parse(std::string path). parse(filesystem::path) accepts strings implicitly.

Tests:
- development fixtures renumbered 2.0.0 -> 1.0.0; development test sources
  swept development:: -> v1::, includes development/ -> v1/.
- v1 community tests migrated from v1::Parser::Parse(path) to top-level parse().

Status: file-list mechanism verified (its structural sub-tests pass). Remaining
14 failures are a single separate gap: community v1 lists phase species as bare
strings ([A, B, C]) but the engine expects objects ([{name: A}]). Tracked next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Accept bare-string phase species in the v1 engine (restore v1 behavior)

The old v1 phase parser accepted a phase's species as either bare strings
(`species: [A, B]`) or objects (`species: [{name: A, ...}]`). The development
engine only handled the object form and threw on scalars. Restore the dual
handling in both ParsePhases (parse) and ValidatePhases (validate): a scalar is
shorthand for the species name; a map carries name + optional properties.

Valid v1 reaction/phase configs now parse. Remaining v1 test failures are a
separate matter (stale error-count expectations + a few validator key-set gaps
such as FIRST_ORDER_LOSS dropping `products`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Restore v1 leniency in the engine: optional/scalar components, FOL products, surface condensed_phase

Reconcile the engine's per-reaction validators/parsers with v1 community files:

- Reaction components may be bare strings (e.g. `gas-phase species: A`), not just
  objects. GetReactionComponentName handles scalars; ParseReactionComponents only
  reads coefficient/comments on the object form (scalar -> name only).
- first_order_loss: restore `products` (optional key + parse it, guarded since
  optional). The type already had the field; old v1 allowed it.
- surface: `condensed_phase` is optional, not required (community surface files
  omit it). Validator only verifies the phase when present; parser reads it when
  present.

No more segfaults. Remaining v1 failures are stale test expectations (the engine
reports richer errors than old v1), to be updated next per decision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop the 6 CAMP-only reactions from v1; delete development tests

These reactions are supported by CAMP (v0) and aren't used by the ODE solver,
so remove them from the v1 engine (re-addable later atop the canonical types):
aqueous_equilibrium, condensed_phase_arrhenius, condensed_phase_photolysis,
henrys_law, simpol_phase_transfer, wet_deposition.

- Delete their parser + validator sources and CMake entries.
- Remove their IReactionParser classes + registry entries from reaction_parsers.hpp.
- Remove their type structs + Reactions members from types.hpp (species-level
  henrys_law_constant_* properties are unrelated and kept).
- Delete all development tests (they had been swept onto the v1 engine during the
  rename and were the only coverage for these now-removed reactions).

Unused validation:: keys for the dropped reactions are left in place (harmless).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update v1 test expectations to the engine's error reporting; fix file-list version-check ordering

- v1 community tests: update error-count expectations to the engine's richer
  reporting (unknown species -> ReactionRequiresUnknownSpecies +
  RequestedSpeciesNotRegisteredInPhase; duplicates report one error per
  occurrence). Drop branched's stale third bad-component assertion.
- ResolveFileConfig: check the file-list minor-version requirement BEFORE loading
  referenced files and return early, instead of also emitting file-not-found
  errors for a config already known to be invalid (matches old v1).

Suite now 25/26. The lone remaining failure (file_configs TwoPhasesSets) is a
semantic-policy question: a gas-phase reaction produces a species defined only in
the aqueous phase, which the engine's phase-membership check rejects.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Exempt reaction products from the phase-membership check

A reaction's products may legitimately reference species defined in another phase
(e.g. a gas-phase reaction producing an aqueous species), so only reactants are
required to belong to the reaction's phase. Reactants stay phase-checked; products
are still required to be known species (unknown-species check unchanged).

- All reaction validators build a reactants-only list for CheckSpeciesPresenceInPhase;
  products go only into the unknown-species check. emission (products only) drops the
  phase check entirely; first_order_loss already checked reactants only.
- Realign v1 test error-count expectations accordingly.

Full suite green: 26/26.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add version-agnostic validate(const Mechanism&)

Semantic validation belongs to the canonical Mechanism, not to a specific parser
version. Add a top-level validate(const Mechanism&) -> Errors that checks the
mechanism's semantic invariants on the struct, independent of how it was produced
(parsed from any version, or built in code):
  - unique species names; unique phase names; unique species within a phase
  - phase species exist in the species list
  - reaction species exist; reactants are registered in the reaction's phase
    (products may reference any phase)

This is the in-code validation path from issue #269's "ideas" (in-code mechanism
-> validate). New unit tests cover an in-code mechanism directly.

Note: additive. parse() still uses v1's YAML-level (line-numbered) validation;
routing parse() through validate(Mechanism) and removing v1's duplicated semantic
checks is a follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refactor validate() onto a single semantics checker over a located intermediate

Introduce semantics::Input (species/phases/reactions as name + optional
ErrorLocation) and ValidateSemantics(Input) as the single place the semantic
rules live. validate(const Mechanism&) now builds a location-free Input and calls
it. Errors carry line:col whenever a source location is supplied — which lets the
parse path reuse the same checker with locations next, instead of duplicating the
rules in v1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add v1 BuildSemanticInput (YAML -> located semantics::Input)

Extracts species/phase/reaction species references from a fully-resolved v1 YAML
node, each carrying its source ErrorLocation, into the version-neutral
semantics::Input. This lets the parse path feed the single ValidateSemantics
checker with line:col (the YAML counterpart to validate(Mechanism)'s location-free
build). Not yet wired into Parser::Validate — that wiring + removing v1's own
semantic checks is the next step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Route parse() semantic validation through ValidateSemantics; strip v1's copy

v1's parsers now own only deserialization + structural (schema/cardinality/
mutually-exclusive) validation. All semantic checks (unknown species, phase
membership, duplicates, unknown phase) are performed once by the version-neutral
ValidateSemantics:

- Parser::Validate runs the structural validators, then calls
  ValidateSemantics(BuildSemanticInput(object)) so parse-time semantic errors keep
  line:col.
- The 12 reaction validators drop their species/phase/duplicate checks (keeping
  ValidateSchema, ValidateReactantsOrProducts, cardinality, Ea-vs-C).
- ValidateSpecies / ValidatePhases reduced to structural schema checks.

Semantic rules now exist in exactly one place and apply to the canonical Mechanism
(parsed or in-code). The old per-component semantic helpers in v1/utils are now
unused (left in place; can be removed in a follow-up). Suite: 27/27.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Modernize integration tests to the parse()/validate() API

Rewrite the integration tests onto the current public API:
- test_parser: use top-level parse() for version dispatch (v0 fallback, v1,
  missing file, unsupported version, v0 directory). Drop the v2/development case.
- test_v0_parser: v0::Parser::Parse still returns expected; update to the
  canonical Mechanism + .error().
- test_v1_parser: files via parse(); in-memory YAML/JSON via the v1 engine
  (Validate + Parse(node)), since v1::Parser no longer has Parse(path)/
  ParseFromString/ParseFromNode.
- test_v1_read_from_file_configs: multi-file configs via parse().
- Drop the stale development_parser CMake entry (source no longer exists).

Known: examples/v1/full_configuration.{json,yaml} is semantically invalid under
the engine's checks (undeclared foo/bar/baz; third-body M used outside the gas
phase), so the two full-configuration tests fail pending an example-data decision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix examples/v1/full_configuration to be semantically valid

The full-configuration example tripped the engine's semantic checks:
- third-body species M was used as a TROE reactant but absent from the gas phase
  -> add M to the gas phase species list
- the TERNARY_CHEMICAL_ACTIVATION reaction referenced undeclared species
  foo/bar/baz -> use declared species (A/B/C)

Species count is unchanged (M was already declared), so the integration test
assertions are unaffected. Full suite green: 31/31.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Remove dead semantic helpers from v1/utils

After semantic validation moved to the version-neutral ValidateSemantics, these
v1/utils helpers had no remaining callers: FindUnknownObjectsByName,
CheckSpeciesPresenceInPhase, CheckPhaseExists, ReportUnknownSpecies,
FindDuplicateObjectsByName, GetSpeciesNames, and the NodeInfo/DuplicateEntryInfo
structs. Remove them. v1/utils now holds only the parser-support helpers still in
use: AsSequence, AppendFilePath, GetComments, GetReactionComponentName.

Full suite green: 31/31.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Tidy v1 type_validators: structural-only docs, drop dead ValidateParticles

- Rewrite the docstrings to reflect that these are structural (schema) checks
  only; semantic invariants moved to ValidateSemantics.
- Remove ValidateParticles (header + definition): its only callers were the
  Henry's-law/SIMPOL validators, which were dropped with the CAMP-only reactions.

Suite green: 31/31.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Rename validation.hpp -> validation_keys.hpp

The header holds the schema key-name constants (the `validation::` namespace), not
validation logic. Renaming disambiguates the three similarly-named headers:
  - validation_keys.hpp  : the key strings
  - validate_schema.hpp  : structural/schema checking
  - validate.hpp         : semantic validation (ValidateSemantics / validate)
The `validation` namespace name is unchanged; only the file + its include sites
(7) are updated. v0/validation.hpp is unrelated and untouched.

Suite green: 31/31.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: add README usage example (parse a file or validate an in-code mechanism)

A single, compile-checked example showing the two entry points around the
canonical Mechanism: parse() a YAML/JSON file (version auto-dispatched, returning
std::expected<Mechanism, Errors>), and build a Mechanism in code then validate()
it with the same semantic checks the parser uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* private headers

* Flatten v1 detail headers to nested namespace; fix ::v typo

Collapse the v1 private headers to the joined `mechanism_configuration::v1`
namespace form and normalize include ordering. Fixes a typo in
type_validators.hpp (`::v`) that broke compilation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: document lambda_rate_constant, add it to examples; drop planning notes

- Add docs/source/v1/reactions/lambda_rate_constant.rst and wire it into the
  v1 reactions index/toctree (it was the only v1 engine reaction undocumented).
- Add a LAMBDA_RATE_CONSTANT reaction to examples/v1/full_configuration.{yaml,json}
  so the example exercises every v1 reaction type (both parse cleanly).
- Remove the scratch planning docs PLAN-consolidate-v1.md and
  TODO-name-species_name-alias.md from the repo root.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Release 2.0.0: bump version and add docs changelog

Bump project version 1.1.2 -> 2.0.0 to reflect the breaking C++ API changes
on this branch (std::expected parse()/validate(), canonical Mechanism, the
public/private header split with a single umbrella header, and removed v1
reactions). The v0/v1 config file formats are unchanged.

Add docs/source/changelog.rst (modeled on music-box's changelog) covering the
API changes, header reorganization, removed reactions, and a migration example,
and wire it into the docs toctree. Update the docs |project_version|.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: fix Ubuntu workflow matrix exclude indentation

`exclude` was nested under `strategy` instead of `strategy.matrix`, which is an
invalid workflow definition — GitHub Actions rejected it at startup, so the
Ubuntu job failed in 0s on every push. Re-indent `exclude` under `matrix`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix Linux clang and MSVC CI builds

Two separate stdlib/include issues that GCC papered over via transitive
includes but Linux clang and MSVC did not:

- MSVC: errors.hpp defines PrintTo(..., std::ostream*) and streams a string
  into it, but never included <ostream>, leaving std::basic_ostream an
  incomplete type (C2027 in <string_view>'s operator<<). Add <ostream>.

- Linux clang: clang-18 paired with libstdc++ (gcc-13/14) does not expose
  std::expected at any -std flag (reproduced in ubuntu:24.04). Build the
  Ubuntu clang job against libc++ (-stdlib=libc++ + libc++-dev), which
  provides std::expected and std::format. Verified: full repo builds clean
  with clang-18 + libc++ for both fmt OFF and ON under -Werror, 31/31 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Rename structural checks Validate -> CheckSchema; reserve validate() for semantics

The word "validate" was overloaded: the public semantic validate(const Mechanism&)
/ ValidateSemantics shared the name with the v1 structural YAML checks, making it
hard to see what the real validation is. Reserve "validate" for semantics and name
the structural layer "CheckSchema":

- Parser::Validate            -> Parser::CheckSchema
- IReactionParser::Validate   -> CheckSchema (+ all 12 reaction overrides)
- ValidateSpecies/Phases/Reactions/ReactantsOrProducts
                              -> CheckSpeciesSchema/CheckPhasesSchema/
                                 CheckReactionsSchema/CheckReactantsOrProductsSchema
- ValidateSchema primitive    -> CheckSchema
- detail/validate_schema.{hpp,cpp} -> detail/check_schema.{hpp,cpp} (+ src/check_schema.cpp)

In-member primitive calls are qualified (mechanism_configuration::CheckSchema) to
avoid the member name hiding the free function. Fix the stale validator docstrings
that claimed to "ensure all referenced species and phases exist" (that moved to
ValidateSemantics) and mark the now-unused existing_species/existing_phases params.

validate()/ValidateSemantics and the validators/ + type_validators.* names are
unchanged. Build clean (gcc-14 -Werror), 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Tidy v1 schema checkers: drop dead is_valid, rename validation_errors

The reaction validators carried an is_valid flag that was either never read
(arrhenius) or only gated a trailing `return errors;` that the function reached
anyway. Where it did guard a later check (first_order_loss/surface/photolysis
cardinality, and CheckReactionsSchema's per-reaction loop), it was equivalent to
`!errors.empty()` at that point, so the flag is replaced by that check.

Also rename the reused local `validation_errors` -> `schema_errors`, matching the
CheckSchema naming and avoiding the "validation" overload (reserved for the
semantic validate()/ValidateSemantics).

No behavior change. Build clean, 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Rename type_validators -> type_schema, reactions/validators/ -> reactions/schema/

These hold structural schema checks (CheckSpeciesSchema, XxxParser::CheckSchema,
etc.), not semantic validation, so "validators" was misleading. Rename to match
the CheckSchema naming and pair cleanly with the sibling reactions/parsers/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Flatten reactions/{parsers,schema}/ into one file per reaction

Each reaction was one IReactionParser subclass (e.g. ArrheniusParser) whose two
methods, CheckSchema and Parse, were split across reactions/schema/X.cpp and
reactions/parsers/X.cpp with near-identical include blocks. Splitting one class
across two files/dirs cost cohesion for no real benefit.

Merge each pair into a single src/v1/reactions/X.cpp holding the whole parser
(CheckSchema then Parse), drop the parsers/ and schema/ subdirectories, and list
the 12 sources directly in reactions/CMakeLists.txt. (taylor_series needed
<detail/constants.hpp>, previously only pulled in on the parser side.)

No behavior change. Build clean (gcc-14 -Werror), 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* v1::Parser::Parse owns validation; return std::expected, hide CheckSchema

Parse(node) now runs CheckSchema (structure + semantics) itself and returns
std::expected<Mechanism, Errors>, so callers can't build from an unvalidated
node or forget to validate. CheckSchema and the construction step (Build) are
now private. The top-level parse() v1 branch collapses to ResolveFileConfig ->
Parse, and the string-parsing test helper to a single Parse call.

No behavior change. Build clean (gcc-14 -Werror), 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* v1::Parser::Parse: add path/string/node overloads; mirror v0 in parse()

Parsing a v1 file no longer requires calling ResolveFileConfig by hand. Parse is
now overloaded:
  - Parse(filesystem::path)  -> resolve file-list sections, validate, build
  - Parse(std::string)       -> treat as a YAML/JSON document, validate, build
  - Parse(YAML::Node, bool)  -> validate + build a loaded node

ResolveFileConfig, CheckSchema, Build, and the config-path setters are now
private; the dead FileToYaml is removed. The top-level parse() v1 branch
collapses to `return v1::Parser{}.Parse(config_path);`, matching v0. Exception
safety moved into Parse(node) (the shared path), so value-conversion throws
surface as Errors rather than escaping.

No behavior change. Build clean (gcc-14 -Werror), 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: use a static Apache-2.0 license badge; fix LICENSE link to main

The github/license shields.io badge is dynamic (hits the GitHub API) and
intermittently fails with "Unable to select next GitHub token from pool",
rendering broken. Switch to a static Apache-2.0 badge that never calls the API,
and point the link at blob/main/LICENSE (the default branch) instead of master.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop read_from_config_file flag from v1::Parser

The boolean only told CheckSchema whether to reset config_path_ (the file-path
prefix for error messages) — a flag argument that callers had to reason about.
Each Parse overload already knows its context, so it now sets config_path_
itself (ResolveFileConfig for files; empty for string/node) and delegates to a
private flag-free ValidateAndBuild(node) that runs CheckSchema + Build. CheckSchema
and Parse(node) lose the bool.

No behavior change. Build clean, 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: fix stale version_mismatch comment

The comment claimed version "1.3.0" / "minor != 1", but the config is "1.0.0"
and the gate is `minor < 1` (file-list requires v1.1+). Correct the comment to
match the config and logic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Hoist semantic validation out of CheckSchema into ValidateAndBuild

CheckSchema also ran ValidateSemantics, so its name undersold what it did and
re-overloaded "validate" in the structural layer. Move the semantic pass up into
ValidateAndBuild, which now reads as an explicit three-phase pipeline:
structural (CheckSchema) -> semantic (ValidateSemantics, gated on clean
structure) -> Build. CheckSchema is now schema-only, matching its name.

No behavior change. Build clean, 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Rename validation key-vocabulary to keys

The validation:: namespaces were just dictionaries of YAML key-name strings,
used by parsing and construction as much as by checking (e.g. Build reads
object[validation::gas_phase]) — so "validation" misdescribed them. Rename
mechanism_configuration::validation -> keys and v0::validation -> v0::keys
(validation_keys.hpp -> keys.hpp, v0/validation.hpp -> v0/keys.hpp), and
validation:: -> keys:: throughout (~600 refs). "validation" is now reserved for
actual semantic validation.

No behavior change. Build clean (gcc-14 -Werror), 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Move the v1 key vocabulary under v1 (keys -> v1::keys)

detail/keys.hpp held the v1 keys but lived at the detail root, asymmetric with
v0's detail/v0/keys.hpp. Its only cross-version use was parse()'s version
dispatch reading the "version" field. Move it to detail/v1/keys.hpp as
mechanism_configuration::v1::keys (v1 call sites are inside namespace v1, so
keys:: still resolves with no change), and have the version-agnostic dispatcher
in parse() read the literal "version" instead of depending on a version's keys.

Now each version owns its key vocabulary: v0::keys and v1::keys.
No behavior change. Build clean (gcc-14 -Werror), 31/31 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Prefix the unsupported-version error with the config file path

The "Unsupported version number" error was the one parse() error without a file
path. We only reach that branch after reading the version out of the file, so it
always names a real document — prefix it (path: message) like the other errors,
so it points at the offending file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Include line:col in the unsupported-version error

GetVersion now also returns the source location of the `version` field (absent
for directories / version-less docs). parse()'s default branch uses it to format
the error as path:line:col error: ... , matching the located v1 errors, instead
of just the file path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* adding readme example parsing

* update to camelcase

* remove old aqueous  and henrys law configs

* update the copyright header

* rearrage the types

* replace phase location with location

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jiwon Gim <jiwongim@ucar.edu>
Co-authored-by: GitHub Actions <actions@github.com>
* clean up the config file for cloud chemistry

* distinguish between micm arrhenius and miam arrhneius

* add miam struct and keys

* code cleanup

* Update test/integration/integration_configs/cam_clound_chemistry.json

Co-authored-by: Kyle Shores <kyle.shores44@gmail.com>

---------

Co-authored-by: Kyle Shores <kyle.shores44@gmail.com>
Co-authored-by: GitHub Actions <actions@github.com>
* Fix code coverage to exclude third-party and system code

Coverage was reporting ~30% because the lcov capture only excluded
test/*, so the totals included the FetchContent dependencies built
under build/_deps/ (yaml-cpp, googletest, optionally fmt) plus inlined
system/libstdc++ headers. The denominator was dominated by untested
third-party code, masking the project's real coverage.

- Add _deps/* and /usr/* to the lcov EXCLUDE list in CMakeLists.txt
  (BASE_DIRECTORY only sets path display, it does not restrict capture).
- Add codecov.yml with an ignore list as a backstop for Codecov's own
  path matching.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Instrument the library for coverage (apply flags before add_subdirectory)

The real cause of the low (~30%) coverage: append_coverage_compiler_flags()
was called after add_subdirectory(src), so CMAKE_CXX_FLAGS only picked up the
coverage flags for the test targets defined later. The src/ library was built
without instrumentation, so it produced no .gcno/.gcda data and the report
covered only a handful of header lines inlined into the test TUs (51 lines).

Move the include + append_coverage_compiler_flags() ahead of
add_subdirectory(src) so the library itself is instrumented. The
setup_target_for_coverage_lcov() target definition stays in the test block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop Fortran-only flags from coverage compiler flags

COVERAGE_COMPILER_FLAGS included -fcheck=bounds,do,pointer and
-ffpe-trap=zero,overflow,invalid, which are gfortran-only. This is a
CXX-only project, so cc1plus rejects them ("valid for Fortran but not
for C++"). It went unnoticed because the flags were previously only
applied to the gtest test targets (no -Werror); now that the library
is instrumented (and built with -Werror), the warnings became errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* add parser accepting string config

* adding yaml test

* changing default gas phase name to gas

* using correct reaction array for each type

* correcting tests

* make fmt private
@K20shores K20shores merged commit 8e1b8e4 into release Jun 30, 2026
55 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants