Restructure mechanism parser (std::expected, separation of public/private headers) and support in-code mechanisms#276
Merged
Conversation
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>
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>
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>
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>
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>
…oducts, 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>
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>
…-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>
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>
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>
…termediate 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>
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>
…'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>
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>
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>
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>
…icles - 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>
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>
…echanism) 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>
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>
…g 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>
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>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #276 +/- ##
===========================================
- Coverage 70.00% 31.57% -38.43%
===========================================
Files 5 4 -1
Lines 10 19 +9
===========================================
- Hits 7 6 -1
- Misses 3 13 +10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`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>
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>
…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>
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>
…ions/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>
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>
…hema 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
boulderdaze
requested changes
Jun 16, 2026
boulderdaze
left a comment
Contributor
There was a problem hiding this comment.
Looks good! I have a few questions and suggestions
std::expected, separation of public/private headers) and support in-code mechanisms
Contributor
|
I updated the title to capture some of the context from the comments for the release notes. Hope that's okay with you! |
boulderdaze
approved these changes
Jun 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #269
parse(which validates schema when parsing)validatewhich checks for structural things (all species on a reaction are in a phase)std::expected<Mechanism, Error>src/detailmake testcall