Skip to main content

Crate gugen

Crate gugen 

Source
Expand description

gugen (具現): explainable materials synthesis and process planning.

This crate turns a target inorganic composition (and optionally a target structure) into candidate precursor sets, balanced reactions, and solid-state process plans — each with its evidence, assumptions, and unresolved conditions kept explicit. It does not predict experimental success. See docs/scientific_scope.md for what is and is not in scope, and CHANGELOG.md for the user-facing capability list.

This implements typed errors, validated numeric types, composition, target specification, the public report schema, provenance, provider trait boundaries, exact reaction balancing, bounded precursor-set search, a solid-state process template, plan scoring/confidence, end-to-end orchestration via Planner, an optional mikiwame structural-diagnostics adapter, a CLI (src/bin/gugen.rs: plan, balance, explain, validate-target, doctor, batch), a validation suite against curated literature fixtures (tests/, docs/benchmark_report.md), and v0.1 release preparation (license files, docs.rs metadata, a dependency license audit — see tasks/todo.md’s Phase 9 section). The chematic-crystal adapter remained blocked on that crate’s publication through v0.1 (Phase 16 below addresses part of this), and two validation findings are documented rather than fixed (see tasks/todo.md’s Phase 8 section). v0.1.0 is published (crates.io, merged to main, tagged v0.1.0) — see tasks/todo.md’s Phase 9 section for the release record. Post-v0.1 development toward v0.2.0 (Phase 10-14 — real literature-sourced process conditions, a large-scale blind benchmark, a second route family, a thermodynamic- provider adapter boundary, and a validation-fixture citation repair) is tracked from tasks/todo.md’s Phase 10 section onward, not AGENTS.md §26 (which only defines the original 9 phases). v0.2.0 is published (crates.io, tagged v0.2.0). Work toward v0.3.0 has begun with Phase 15A (route_suitability module): a report-level evidence model for whether a route family suits a target (Supports/Contradicts/ Unknown findings, never an aggregated score). Phase 15B added derive_recommendation, a pure function deriving a discrete RouteRecommendation from that evidence, and wired only its NotRecommended state into Planner::plan: a plan with strong, uncontested contradicting evidence is moved from plans into the new SynthesisPlanningReport.not_recommended (kept, with its findings, not dropped), and a target where every generated plan is excluded this way abstains explicitly via unresolved rather than returning an empty success. No numeric score is affected by either phase. Phase 16 added an optional chematic_crystal feature: to_mikiwame_structure converts a caller-supplied chematic_crystal::PeriodicStructure (now published, 0.15.0) into a mikiwame::OwnedStructure, closing the specific conversion gap the mikiwame adapter had named since Phase 6. Still not auto-wired into Planner::planTargetSpecification has no geometry field, so a caller still applies the result themselves. Phase 17 audited how much literature evidence for route suitability actually exists in a real synthesis corpus, and evaluated derive_recommendation against a hand-verified holdout record – explicitly not a route-family prediction-accuracy benchmark (see docs/route_suitability_corpus_audit.md); no production code changed. Phases 15A/15B/16/17 together are v0.3.0’s planned development work. v0.3.0 is published (crates.io, tagged v0.3.0). Post-v0.3.0 development toward v0.4.0 added finite-temperature Gibbs-energy estimation for gas-free solid systems (Phase 19P/19P.1), deliberately not connected to ranking (thermodynamic_support stays None); a bulk literature-corpus snapshot loader and exact-match observation provider (Phase 20B); cross-DOI field comparison across independent sources for that corpus (Phase 20C); a manual extraction-accuracy audit against original source papers (Phase 20D); and Integration, which surfaces that cross-DOI evidence on SynthesisPlan.literature_evidence for reference-only display – never auto-filling ProcessStep conditions and never affecting score/confidence/ranking. Together these are v0.4.0’s planned development work: gas-free solid finite-temperature thermodynamic primitives, a bulk literature observation snapshot API, cross-DOI agreement/conflict classification, and reference-only literature evidence in Planner – an evidence-infrastructure release, not a ranking-accuracy or synthesis-success-prediction claim. v0.4.2 is published (crates.io, tagged v0.4.2) – adds the optional, off-by-default commercial_catalog feature (Commercial Precursor Catalog: matches an existing SynthesisPlan’s precursors against a caller-supplied catalog of commercial offers, as post-planning processing that never affects the plan’s score, confidence, reaction, or process steps). Phase 23 (v0.5.0) is a breaking, core-API-hardening release, independent of the commercial catalog work: BalancedReaction/ReactionSpecies close a validation-bypass gap (private fields, a required element- conservation check, Phase 23A); Planner::builder(...) covers any combination of the 4 optional providers, deprecating the 5 named constructors that didn’t (Phase 23B); the existing provider-call dedup extends to ProcessEvidenceProvider::precedents (Phase 23C); and 5 enums are marked #[non_exhaustive] where their own doc comments already anticipated new variants, alongside a new docs/api_stability_policy.md (Phase 23D). v0.5.0 is published (crates.io, tagged v0.5.0). Phase 24 makes the commercial-catalog feature (v0.4.2) usable by a researcher directly, not just from Rust code: a gugen commercial-plan CLI subcommand (24A); declarative CSV column-name mapping for real-world supplier exports via CommercialCatalogColumnMap (24B); and named procurement ranking policies via CommercialRankingPolicy (Balanced/CostFirst/ LeadTimeFirst/PurityFirst/MinimumUnresolvedData/Pareto, 24C). Phase 25 adds SynthesisExecutionRecord: an append-only, versioned- schema record of what actually happened when a gugen-proposed plan was attempted in a real lab, structurally separate from Planner/ score_plan by construction. Phase 26 surfaces those records back during planning as reference-only PriorExperimentEvidence on SynthesisPlan (a new 5th optional provider, PlannerBuilder::prior_experiment_evidence_provider(...)) – same display-only discipline as the existing literature-evidence integration: never a success rate, never fed into score, confidence, or ranking. v0.6.0 is published (crates.io, tagged v0.6.0).

Phase 31 adds SynthesisRoute/search_two_step_routes: validated, stoichiometrically connected two-step (precursor → intermediate → target) routes for targets a one-step search can’t reach within budget – a primitive, not an accuracy claim; intermediate_candidates is always caller-supplied and Planner never invokes this automatically. The same phase’s real-corpus testing found and fixed a spurious identity-reaction acceptance bug in search_precursor_sets (a candidate matching a curated_byproducts() composition exactly could be accepted as a no-op unrelated to the target). An optional, explicitly experimental experimental_grammar feature (default off) adds hand-written intermediate-candidate decomposition grammars (transformation_grammar module); measured against the same corpus, they did not recover any target beyond a plain corpus-frequency prior, so this is not yet promised-stable API. v0.7.0 is published (crates.io, tagged v0.7.0).

v0.8.0 is a bug-check-and-refactoring sweep: SolidThermodynamicEntry’s two numeric fields are now private (closing the same validation-bypass gap Phase 23A closed for BalancedReaction/ReactionSpecies – breaking), HydroxideToOxideGrammar’s missing metal-presence guard is fixed, and process.rs’s condition-conflict-resolution code moved to condition_precedents.rs (no public API change). See CHANGELOG.md for the user-facing summary.

Structs§

AcceptedPrecursorSet
One accepted candidate plan: which precursors, and the balanced reaction that resulted (AGENTS.md §10’s byproduct handling is folded in here – reaction may include a curated byproduct the search had to introduce to make the elements balance).
AcidCarbonatePhosphateGrammar
2 H3PO4 + M2CO3 -> 2 MH2PO4 + CO2 + H2O, restricted to phosphoric acid (exact H:P:O = 3:1:4 signature, no other elements) paired with a monovalent-metal carbonate (exact M:C = 2:1, no hydrogen, and an exact O:C = 3:1 – deliberately stricter than CarbonateToOxideGrammar’s own carbonate check, which accepts O:C >= 3:1 to also correctly handle oxycarbonate-like compositions with extra oxide oxygen beyond the carbonate group; this grammar produces a fixed-formula guess rather than a pure mass-balance derivation, so the tighter, exact signature is the safer choice here). The proposed composition is always the fixed monobasic-phosphate formula unit MH2PO4, independent of the pair’s actual relative amounts (matching every other grammar here: a formula-unit-shaped candidate, not a balanced-reaction claim). Does not attempt di-/tri-basic phosphate salts, does not handle divalent/trivalent carbonate metals, and does not generalize to other oxo-acids despite the “acid+carbonate” pattern – narrowed deliberately to the one real case this grammar is modeled on (DOI 10.1016/j.tca.2014.08.028, see this phase’s own doc).
ActualPrecursorAmount
What was actually weighed for one precursor. Deliberately not process::MaterialAmount – that type’s formula_units: u64 is required and mass_grams optional, the right shape for a planned amount computed from stoichiometry; a lab log needs the reverse emphasis, since an operator weighs grams, not formula units.
ActualProcessStep
One real process step actually performed. planned_step_index is the position of the corresponding step in SynthesisPlan.steps, when this step corresponds to one gugen proposed – None for an ad-hoc step the operator performed that wasn’t in the plan (itself worth a Deviation { category: DeviationCategory::SequenceDeviation, .. }).
ApplicabilityAssessment
AvailabilityMetadata
Minimal placeholder for provenance about where/whether a precursor is obtainable. AGENTS.md §9 lists availability as a filter candidate but doesn’t specify a shape; kept intentionally small until a real provider exists. Missing availability must not block a precursor from being used – it’s a gap in metadata, not evidence the compound is unavailable (AGENTS.md §21.2’s “availability metadata欠損” test case).
BalancedReaction
An element-balanced reaction with integer, gcd-normalized coefficients (AGENTS.md §10). The exact-rational null-space solver that produces these from a target/precursor set is Phase 2 work (docs/architecture.md); this type is the Phase 1 foundation it returns into.
CandidateGeneratorEnsemble
Combines multiple CandidateGenerators into one candidate list via min-rank fusion, and implements PrecursorCatalog itself – so an ensemble is a drop-in Planner::builder catalog argument, requiring zero changes to Planner/PlannerBuilder. PR 1 does not wire this into Planner; every measurement in this PR calls the ensemble directly, the same way every existing exploration-recall benchmark already bypasses Planner and calls search_precursor_sets directly.
CarbonateToOxideGrammar
MCO3 -> MO + CO2 (per carbonate carbon: -1 C, -2 O, all other elements unchanged). Narrowed to compositions with no hydrogen, deliberately excluding bicarbonates and other mixed H+C+O species rather than guessing which decomposition applies to them.
CasNumber
A CAS Registry Number as supplied by a catalog row. checksum_verified distinguishes “checksum verified” from “checksum failed or the string isn’t CAS-shaped at all” – Phase 22 never uses CAS as a basis for chemical-identity matching (composition is), so a malformed CAS is recorded, not rejected.
CatalogExactGenerator
Wraps an existing InMemoryPrecursorCatalog as a CandidateGenerator – “exact” means literally whatever the catalog returns, in its own element-overlap-filtered order, stamped with rank = output position. Near-zero new logic: reuses InMemoryPrecursorCatalog’s existing filter/sort/dedup verbatim rather than reimplementing it.
CommercialCatalogColumnMap
A declarative mapping from gugen’s canonical CSV column names (e.g. formula, manufacturer) to the header names an actual supplier’s export file uses (e.g. Chemical Formula, Supplier) – lets CommercialPrecursorCatalog::load_csv_with_column_map accept non-standard headers without inventing per-manufacturer adapters. Only the columns that differ need an entry; anything omitted is looked up under its canonical name as usual.
CommercialCatalogLoadReport
CommercialCombination
Serialize only – see CommercialOfferSelection’s doc comment (selections transitively carries its &'static str fields).
CommercialExclusion
CommercialOfferId
CommercialOfferSelection
Serialize only, deliberately no Deserialize: unresolved_fields is Vec<&'static str>, which cannot deserialize into a non-'static borrow from an arbitrary input buffer (the general reason every &'static str-bearing type below is Serialize-only, matching this crate’s existing precedent for output-only report types – reaction.rs/process.rs/thermodynamics.rs/score.rs).
CommercialPlanAssessment
Serialize only – see CommercialOfferSelection’s doc comment (combinations/unresolved_commercial_fields transitively carry &'static str fields).
CommercialPlanningConfig
CommercialPlanningRequest
CommercialPrecursorCatalog
A loaded, deduplicated commercial-offer catalog. offers is private (unlike SynthesisPlan’s all-pub convention): offer_id uniqueness is a real invariant this type guarantees – it is what makes the combination search’s final tie-break key total.
CommercialPrecursorOffer
CommercialWarning
Its own type (not a reuse of PlanningWarning), deliberately – keeps this module’s return type structurally separate from anything score_plan could ever consume, reinforcing at the type level that commercial data can never leak into scientific scoring.
CompetingPhase
A candidate phase’s formation energy, offered for context alongside a BalancedReaction – e.g. “would this target’s elements more readily form some other known compound instead” (Phase 13, ThermodynamicProvider::competing_phases). Additive to ThermodynamicProvider, not to ReactionEnergy: that type’s own doc comment forbids growing unrelated fields onto it specifically, but says nothing against a sibling type for a genuinely different quantity. gugen does not compute a selectivity/likelihood score from this data (AGENTS.md §4.3) – it is surfaced only as PlanningEvidence.
Composition
An elemental composition: element -> stoichiometric/formula amount. Amounts must be finite and strictly positive; the composition must contain at least one element. Iteration order is always by element symbol (via BTreeMap), so results built from a Composition are invariant to the order elements were supplied in (AGENTS.md §21.4).
ConditionConflict
ConditionPrecedent
One provider’s structured, citable evidence for how a specific Heat step’s conditions should be resolved (Phase 10; AGENTS.md §7/§21.3). Every field the provider doesn’t actually have real, sourced data for stays None – never fabricated to fill a gap. evidence_kind, strength, and source_id are set by whichever provider returns this, not assumed by the planner: ProcessEvidenceProvider is also the trait a user-supplied lab-precedent source implements (EvidenceKind::UserProvidedPrecedent), so a curated-literature-only assumption in the planner would mislabel provenance for every other kind of implementation.
ConfidenceAssessment
AGENTS.md §16, verbatim. Kept as four independent dimensions rather than collapsed into overall, specifically because a reaction can be stoichiometrically certain while its process conditions are completely unresolved (“条件未確定でも反応式が確実なケースがあります。単一 confidenceに潰さないでください”).
CorpusHeatingObservation
One reported heating operation from the literature corpus: a target, the precursor set used to reach it, and whatever this operation’s temperature/duration/atmosphere conditions resolved to – None for any field the source paragraph didn’t report, or reported ambiguously (see the module doc comment). Never carries a HeatingPurpose – see this module’s doc comment for why that is structural, not conventional.
CorpusManifest
Identifies the snapshot file itself – source corpus, the offline build’s own release/checksum, and gugen’s schema version. checksum and release are informational provenance only: gugen never has access to the original upstream corpus file to verify checksum against, so it is recorded, not independently re-verified. record_count is actively checked at load time (must equal the actual number of observation entries in the file), since that catches a truncated or corrupted snapshot cheaply without needing the upstream source at all.
CuratedConditionRecord
One hand-verified literature condition record (AGENTS.md §21.3: never authored from memory). target/precursor_ids identify which route this applies to; conditions carries the actual per-step temperature/ duration/atmosphere data, each traceable to a real citation via its own ConditionPrecedent.source_id.
CuratedSuitabilityRecord
One hand-verified route-suitability record (AGENTS.md §21.3: never authored from memory). Keyed on (target, route_family), not on a precursor set – unlike CuratedConditionRecord (literature_conditions.rs), suitability doesn’t depend on which precursor combination was chosen.
CurrencyCode
A 3-letter uppercase ASCII currency code. Format-validated only, not a full ISO 4217 whitelist: unlike Element (which must reject typos against a closed table because the crate needs to refuse invalid symbols outright), the only thing this module’s arithmetic needs from a currency is “is this the same currency as that one” – format validation plus preventing cross-currency summing satisfies that without a table that would go stale.
DecompositionComparison
One named alternative assemblage compared against a target, and the resulting margin – the decomposition_margin_ev_per_atom result paired with the caller’s own free-text label for what it compared against (e.g. "BaO + TiO2"), since the raw function alone has no way to say what alternative_assemblage was once collapsed into a single Option<f64>.
DedupedProposal
A composition proposed by one or more grammars, after propose_all deduplicates identical compositions across grammars and retains every contributing grammar’s id.
Deviation
DurationRange
Element
A validated element symbol. Construction is the only way to get one, so every Element in the crate is guaranteed to be a real periodic-table symbol. Matching is case-sensitive ("Co" cobalt vs "CO", which is not a valid symbol at all) — a catalog or fixture file using non-standard casing will fail to parse rather than being silently reinterpreted.
EnsembleOutput
Combined output of every generator in a CandidateGeneratorEnsemble run, per design principle 5 (every branch/provider outcome stays distinguishable, never silently dropped): candidates is what a PrecursorCatalog caller (e.g. search_precursor_sets) actually consumes, provenance keeps every generator that proposed each id, and generator_errors keeps every generator that failed outright, labeled by which one.
ExecutionCharacterization
Facts about the resulting material, as measured/reported – every field left None when not actually measured, never guessed. Plain f64, not commercial_catalog::PurityFraction: that type only exists behind the optional commercial_catalog feature, and this module is deliberately feature-independent (see SynthesisExecutionRecord.selected_commercial_offers’s own doc comment).
ExecutionProvenance
What “provenance mandatory” requires at minimum for a record entered by a human after a real lab experiment, not generated by the deterministic core.
ExecutionRecordLoadReport
FrequencyPriorGenerator
Proposes precursors ranked by a caller-supplied frequency table – never computed or bundled by this crate itself, matching the established “caller supplies the data, core never fetches/bundles it” convention (ThermodynamicProvider/MaterialsProjectSnapshotProvider’s own precedent). A caller can build the table from anything: their own literature database, LiteratureObservationCorpus, or a benchmark’s own precursor-formula counts.
GeneratedCandidate
One precursor candidate as proposed by exactly one generator. This is where “full provenance” actually lives – deliberately a wrapper type, not a field added to PrecursorCandidate (not #[non_exhaustive], adding a field there would be a breaking change). rank is a plain ordinal (0 = the generator’s own top pick), never a float/confidence: a generator’s internal priority can never be read as a success probability, because the type has no score field to misuse (mirrors SearchPriority’s own score/priority separation, src/precursor.rs).
GeneratorId
A generator’s stable identity, stamped onto every GeneratedCandidate it produces and used to label a failed generate() call in EnsembleOutput::generator_errors. A string newtype rather than an enum: PR 1 only populates 2 of the eventual 6 named generators, and an enum with unbuilt variants would force a premature #[non_exhaustive] decision the crate’s own API stability policy reserves for types whose doc comment already states a growth expectation. Adding generator #3 later never forces a semver decision this way.
GrammarId
A grammar’s stable identity, stamped onto every ProposedIntermediate it produces. A string newtype, not an enum – same rationale as GeneratorId (src/candidate_generator.rs): only 4 of an open-ended family are implemented here, and an enum with unbuilt variants would force a premature #[non_exhaustive] decision.
HydroxideToOxideGrammar
M(OH)n -> MO(n/2) + (n/2) H2O, identified by an exact O:H = 1:1 ratio (every OH- group contributes exactly one O and one H). Narrowed to compositions with no carbon, to avoid confusion with hydrated carbonates or other mixed C+O+H species. Requires at least 3 distinct elements (metal + O + H), matching CarbonateToOxideGrammar’s own len() < 3 guard – without it, a metal-free 2-element O:H=1:1 composition (e.g. H2O2) would pass this grammar’s own ratio check and produce a bare-oxygen “proposal” with no metal for the claimed hydroxide-decomposition mechanism to apply to (the same missing-guard bug class NitrateToOxideGrammar’s own hydrogen exclusion was added to close, for HNO3).
InMemoryExecutionRecordProvider
The one real PriorExperimentEvidenceProvider implementation this crate ships: an in-memory index over a caller-supplied Vec<SynthesisExecutionRecord> (already parsed by the caller, e.g. via parse_execution_records – this type performs no file I/O and no JSON parsing itself). Mirrors LiteratureObservationCorpusProvider’s own architecture: grouped once at construction into a BTreeMap keyed by the exact identity triple (Composition doesn’t derive Hash, so BTreeMap, not HashMap, is required here, same constraint that provider has).
InMemoryLiteratureConditionProvider
ProcessEvidenceProvider backed by a small, hand-verified set of real, cited firing conditions (Phase 10), expanded from the same 5 literature-cited routes tests/validation.rs already uses for precursor-set recovery. Not a general literature-mining pipeline: that’s a different trust tier (bulk statistical corpus vs. individually verified citable evidence), left to a future large-scale benchmark rather than mixed into this small, hand-checked set.
InMemoryPrecursorCatalog
In-memory PrecursorCatalog (AGENTS.md §8: in-memory/JSON/fixture providers are the v0.1 priority, no network access). Candidates are kept sorted by PrecursorId regardless of construction order, so candidates_for results – and everything built on them – are invariant to catalog insertion order (AGENTS.md §21.4).
InMemoryRouteSuitabilityProvider
RouteSuitabilityProvider backed by a small, hand-verified set of real, cited findings (Phase 15A, expanded once since). Deliberately minimal: this is still not a comprehensive suitability database – see this module’s curated_records() doc comment for what each record proves and what’s intentionally deferred.
Kelvin
A validated temperature within the range Bartel et al. 2018 actually validated the SISSO descriptor against, [300.0, 1800.0] K (deliberately narrower than pymatgen’s [300.0, 2000.0] K – see this module’s doc comment).
LiteratureObservationCorpus
A loaded, deduplicated, deterministically-ordered snapshot of the corpus, queryable by exact target and exact precursor set. Does not implement ProcessEvidenceProvider or any other Planner-facing trait – see the module doc comment for why that connection is structurally unavailable, not merely unwired.
LiteratureObservationCorpusProvider
Adapts LiteratureObservationCorpus::cross_doi_comparisons to the ungated LiteratureEvidenceProvider trait. Computes cross_doi_comparisons() exactly once, at construction time, and indexes the result by route – a per-plan route_evidence() call is an O(log n) BTreeMap lookup, never a fresh corpus-wide pass. This is what makes calling it once per candidate plan (as Planner::plan does) affordable; measured for real in examples/literature_evidence_integration_report.rs.
LiteratureRouteEvidence
A RouteObservationAssessment plus the disclosures a consumer needs to not overread it – what Planner attaches to a SynthesisPlan when a LiteratureEvidenceProvider is configured and finds matching evidence for that plan’s exact route. Reference-only: never auto-applied to ProcessStep conditions, never fed to score_plan, never converted to a ConditionPrecedent.
LoadReport
What happened during a LiteratureObservationCorpus::load call. A complete accounting of the input: accepted + rejected.len() always equals the snapshot’s observations array length – every input record either parsed successfully (contributing to accepted) or didn’t (contributing to rejected), mutually exclusively. duplicates_collapsed is not a third disjoint count on top of those two – it’s a subset breakdown within accepted, counting how many of the successfully-parsed entries were then found to be duplicates of another and collapsed. accepted itself is counted before deduplication (how many entries parsed successfully at all) – it is deliberately not the same number as the final loaded corpus’s own len(), which is accepted - duplicates_collapsed; a reader wanting “how many end up queryable” wants the corpus’s len(), not this field.
MaterialAmount
AGENTS.md §6’s Weigh step. mass_grams is None until gugen has an atomic-weight table (not built yet, no §26 phase currently owns it – see tasks/todo.md); formula units alone already say what’s being weighed relative to the rest of the plan.
MaterialsProjectSnapshotProvider
A ThermodynamicProvider over a fixed, caller-supplied snapshot of competing-phase formation energies – e.g. a pre-fetched slice of a Materials Project query. See the module doc comment: this type never fetches anything itself, and has no notion of “stale” or “refresh”.
Money
Money as integer minor units – never f64, which has no checked_* arithmetic. Any (minor_units, currency) pair is valid once currency itself validated, so construction is infallible.
NitrateToOxideGrammar
M(NO3)n -> MO(n/2) + n "NOx" – the oxide side only, derived from charge balance (n positive charges on M need n/2 O2-), never fixing which nitrogen oxide leaves (balance()’s own curated_byproducts() already includes NO2 and settles that side independently). Narrowed to compositions with exactly one non-N/non-O/non-H element and no hydrogen – excluding hydrogen specifically rules out nitric acid (HNO3) and hydrated nitrates, which are not metal nitrates and for which “the metal” would otherwise wrongly resolve to hydrogen itself.
NotRecommendedPlan
A plan excluded from the recommended list by route-suitability findings (Phase 15B) – plan is unchanged from what would otherwise have appeared in SynthesisPlanningReport.plans (same score/confidence/ evidence, since filtering happens after scoring, not instead of it); contradicting_findings is just the Contradicts findings that triggered exclusion (not the full assessment, which may also carry Supports/Unknown findings for other purposes).
OfferProvenance
PackageMass
A package size, canonically stored in grams. from_milligrams/ from_kilograms are convenience constructors for spec’s minimum “mg/g/kg” requirement – volume packaging is out of scope for Phase 22.
ParticleSizeRangeUm
PhaseRequirement
PlanAssessment
Everything score_plan computes for one plan, ready to be merged into the rest of a SynthesisPlan.
PlanId
Deterministic plan identifier (AGENTS.md §20: “plan IDを決定的にする”). Phase 5 derives this from plan contents; Phase 1 only needs the type.
PlanIdentity
A synthesis attempt’s own long-lived identity – self-describing without needing the originating SynthesisPlanningReport file to still exist. PlanId alone is not enough (it’s an opaque hash, not reversible back to route family/composition); Phase 26’s own matching criteria (target composition + canonical precursor set + route family
PlanScoreBreakdown
AGENTS.md §13, verbatim. Most of this breakdown is structurally constant across every plan the crate can currently produce: stoichiometric_validity and precursor_coverage are always 1.0 (reaction balancing is exact and search_precursor_sets already hard-filters on full element coverage – both are re-derived defensively here rather than assumed, but neither can discriminate between plans yet); thermodynamic_support is always None – with no ThermodynamicProvider configured there’s simply no data, and even with one configured (Phase 13’s MaterialsProjectSnapshotProvider, for one) a resolved reaction energy still isn’t converted into this score, deliberately (see score_plan’s own doc comment); safety_penalty is always 0.0 (no hazard data source exists – see manual_review_required on PlanAssessment). uncertainty_penalty was always 1.0 before Phase 10 (no condition was ever resolved); with a ProcessEvidenceProvider that actually resolves conditions (e.g. InMemoryLiteratureConditionProvider) wired in, it varies for the targets that provider has real cited coverage for – still 1.0 whenever no condition provider is configured, or when one is configured but has no matching precedent for this target. evidence_strength uses weakest-link aggregation (see strength_value) and is 0.25 for every plan the current generator produces, since every route attaches at least one Weak template-default entry – this stays true regardless of condition resolution, since resolved-condition evidence doesn’t remove the template’s own baseline Weak entries. total_ranking_score varies with process_simplicity always, and with uncertainty_penalty only for targets a condition provider actually covers. Since Phase 12, process_simplicity is computed against a per-RouteFamily step-count range (step_bounds), not one shared range – a plan’s route family can therefore change its process_simplicity (and so total_ranking_score) relative to a same-precursor-set plan under a different route family, but this is still the same one real driver, not a new independent dimension: two plans that each happen to sit at their own family’s maximum step count still score identically (see the worked BaTiO3 example in README.md, where both route families tie at 0.0625). This is the true extent of v0.1/v0.2.0’s ranking discriminating power; it is not a seven-dimensional judgment yet.
PlannedStep
Pairs a ProcessStep with its StepRequirement. AGENTS.md §11 mandates this per-step distinction, but §6 shows SynthesisPlan.steps as a bare Vec<ProcessStep> with nowhere to carry it – SynthesisPlan uses Vec<PlannedStep> instead to satisfy both.
Planner
Orchestrates every subsystem built in Phases 2-5 into the single public entry point AGENTS.md §18 illustrates: catalog lookup, bounded precursor search, process templating, and scoring, assembled into one SynthesisPlanningReport.
PlannerBuilder
Builds a Planner with any combination of its 5 optional providers (v0.5.0, Phase 23B) – catalog/config are required up front (there is nothing to plan from without a catalog), each provider is attached by name in any order or combination, and build() is infallible (no constructor, named or builder, performs any validation beyond field assignment). The crate’s first builder pattern; created because the 5 named constructors below only covered 3 of the real 2+-optional-provider combinations.
PlanningAssumption
AGENTS.md §6’s SynthesisPlan.assumptions. Not given a verbatim shape. score_plan populates this only with premises that aren’t already surfaced as a PlanningEvidence.limitations entry or a PlanningWarning (most of the v0.1 generator’s defaults are – e.g. “method choice is a fixed template default” – so this stays short).
PlanningConfig
Top-level planner configuration (AGENTS.md §18).
PlanningConstraints
User-supplied constraints on the planning search. Deliberately minimal in Phase 1 — Phase 3 (precursor enumeration) adds the rest of the filters listed in AGENTS.md §9 alongside the search that consumes them.
PlanningEvidence
AGENTS.md §7. source_id must never carry a fabricated DOI, paper title, patent number, or URL – only what an evidence provider actually returned.
PlanningProvenance
Everything needed to answer “why did this report look the way it did” without re-deriving it from logs (AGENTS.md §7).
PlanningWarning
PrecursorCandidate
Redox-compatibility, atmosphere-compatibility, and hazard/toxicity metadata are out of Phase 3’s scope (AGENTS.md §26 Phase 3’s checklist doesn’t list them; they belong to later process/safety phases) and are not modeled here yet.
PrecursorId
PrecursorSearchOutcome
Result of a bounded precursor search (AGENTS.md §9). rejected always carries a reason for every candidate set the search actually evaluated and turned down. If the search stopped early because SearchBudget::max_precursor_sets was exhausted, rejected also carries one sentinel entry with RejectionCode::SearchBudgetExhausted and an empty precursors list – distinguishing “we looked and found nothing” from “we ran out of budget before looking everywhere” (AGENTS.md §9: “budget不足を「候補なし」と混同してはいけません”).
PrecursorSelection
One precursor’s role in a candidate plan.
PressureRange
PriorExperimentEvidence
Every SynthesisExecutionRecord whose plan_identity matches one plan’s exact (target, canonical precursor set, route family) – Planner attaches this to SynthesisPlan when a PriorExperimentEvidenceProvider is configured and finds a match. Records are kept in whatever order the provider returned them (an append-only log’s natural order is chronological) – never sorted or filtered here. Process conditions, selected commercial offers, and catalog provenance differ freely between records and are shown as-is, not compared against each other or against this plan’s own (usually unresolved) conditions – see Self::outcome_tally’s own doc comment for why this is never a success rate.
ProcessPrecedent
ProcessEvidenceProvider output (AGENTS.md §8). description is free text with no structure – still valid on its own for a provider that only has prose precedent to offer. conditions (Phase 10) carries structured, per-purpose temperature/duration/atmosphere/ramp data, each entry traceable to its own citation; empty for a prose-only precedent.
ProcessTemplateResult
Output of conventional_solid_state_template: everything Phase 4 can determine about a solid-state route for one accepted precursor set, ready to be folded into a SynthesisPlan.
ProposedIntermediate
One candidate intermediate composition, as proposed by exactly one grammar from a set of real input precursor compositions. Mirrors GeneratedCandidate‘s single-source-provenance shape (src/candidate_generator.rs) – combining multiple grammars’ output happens one layer up, in propose_all.
PurityFraction
A validated purity fraction, 0 < x <= 1. Not Score01: Score01 allows 0.0 (a meaningful “no support” score elsewhere in the crate), while a 0.0 purity is meaningless/rejectable here, and nothing else in the crate reuses Score01 outside score_plan’s own domain.
RampRateRange
RankingWeights
AGENTS.md §13, verbatim fields.
ReactionEnergy
Deliberately carries only the energetic quantity. AGENTS.md §4.3 requires thermodynamic favorability to stay separate from experimental likelihood, so this type must not accumulate unrelated “likelihood” fields later.
ReactionSpecies
RejectedCandidate
RejectedObservation
One observation entry that failed to parse, Lenient-mode only. position is this entry’s index within the snapshot’s observations JSON array (0-based) – the entry may have failed before enough of it parsed to recover a corpus_record_index, so this is the only coordinate guaranteed available for every rejection.
RejectedOffer
RouteObservationAssessment
One route (target + precursor set + route family) with at least one StepGroupAssessment backed by 2+ independent DOIs. Routes with no cross-DOI replication anywhere are never emitted – see LiteratureObservationCorpus::cross_doi_comparisons.
RouteSuitabilityAssessment
One route family’s suitability picture for a target – deliberately a Vec, never an aggregated single verdict, so contradictory findings are never force-merged (the owner’s explicit Phase 15A instruction). Empty findings means insufficient_evidence, not “route rejected” – absence of evidence must never be read as evidence of unsuitability (AGENTS.md §13’s existing rule – no evidence lowers confidence, it doesn’t reject – applied to route suitability specifically). Nothing in score.rs reads this type in Phase 15A: it carries no ranking weight yet.
Score01
A validated score in [0.0, 1.0]. Not given a concrete shape by AGENTS.md (only referenced as Score01) – a rejecting newtype matches every other validated numeric type in this crate (TemperatureRange, ReactionEnergy, …).
SearchBudget
Bounds on the deterministic precursor-set search (AGENTS.md §9). Exhausting the budget must be reported, never silently treated as “no candidates” (AGENTS.md §9: “budget不足を「候補なし」と混同してはいけません”).
SearchBudgetSummary
SearchDiagnosticTrace
Diagnostic-only result (Phase 30.5, search_diagnostics feature) – see search_precursor_sets_diagnostic’s own doc comment. Targeted at one caller-known “gold” precursor set rather than a general per-state pop log, which would be unboundedly large across a full factorial sweep over a real corpus.
SolidThermodynamicEntry
A caller-supplied 0 K formation enthalpy and crystal-structure volume for one solid phase, plus which dataset/release/correction-scheme it came from – the Phase 19P input type, deliberately new rather than an extension of CompetingPhase (see ThermodynamicDatasetIdentity’s doc comment). gugen never fetches this data itself; the caller has already queried a real thermochemical/structural database (e.g. Materials Project) and pre-fetched it, mirroring MaterialsProjectSnapshotProvider’s existing contract.
SourcedValue
One distinct value among 2+ conflicting independent reports, with one representative contributing DOI (the alphabetically-first DOI that reported this exact value, for determinism) – mirrors process.rs’s own FieldResolution::Conflict shape, which also keeps one source per distinct value rather than every contributor.
StepGroupAssessment
A positional comparison across independent DOIs, conditioned on one operation shape – never “the route’s step N,” always “among independent DOIs whose heating was extracted with this many steps, step N.” source_dois is every distinct DOI that contributed any field at this key (a superset of any one field’s own contributors, since not every DOI reports every field).
StepGroupKey
The grouping key within one route – target/precursors/route_family are already fixed by the enclosing RouteObservationAssessment, so only the operation shape and position remain. Two step groups with the same operation_index but different heating_operation_count are never the same key – see literature_observation_conflicts’s module doc comment for why that’s the whole point.
StructuralDiagnosticEffects
Effects one mikiwame::MaterialDiagnosticReport should have on gugen planning, decided by a caller that has structure data to analyze.
SuitabilityFinding
One independent piece of evidence about whether route_family suits a target. Reuses EvidenceStrength/EvidenceScope from evidence.rs rather than inventing parallel enums (AGENTS.md §7’s closed-vocabulary discipline applies here too).
SynthesisExecutionRecord
One synthesis attempt’s full record. See the module doc comment for the append-only/versioned-schema/reference-only-later principles this type exists under.
SynthesisPlan
A candidate synthesis plan (AGENTS.md §6). steps is Vec<PlannedStep> rather than the bare Vec<ProcessStep> AGENTS.md §6 shows, so each step can carry the StepRequirement §11 mandates. manual_review_required isn’t in §6’s snippet, but §15 requires the v0.1 JSON plan to carry it (or an equivalent) regardless – see crate::score_plan for why it’s always true in v0.1.
SynthesisPlanningReport
SynthesisRoute
An ordered sequence of BalancedReaction stages, where each stage after the first consumes only compositions already “available” – a base precursor, or a product of an earlier stage. A BalancedReaction is already a valid hyperedge (many reactants -> many products); this type’s only job is enforcing that a sequence of them forms a genuinely connected route to target, not an arbitrary bag of unrelated reactions.
TargetSpecification
TargetStructure
Minimal structural hint about the target: free text, deliberately. chematic-crystal is now published and, with the chematic_crystal feature enabled, crate::to_mikiwame_structure can convert a real chematic_crystal::PeriodicStructure into something mikiwame::analyze consumes (Phase 16) – but that conversion is a standalone, caller-driven bridge, not a field here. Giving TargetStructure itself a geometry field would need real Planner/report-level wiring decisions (how a report should represent per-target structural diagnostics) that Phase 16 deliberately left out of scope; see docs/integration.md. gugen does not implement its own crystal- structure representation either way (AGENTS.md §5).
TargetSummary
TemperatureRange
ThermodynamicConditions
Minimal Phase 1 placeholder for ThermodynamicProvider inputs; extended once a real provider is implemented (Phase 2/6).
ThermodynamicDatasetIdentity
Identifies the specific dataset a batch of SolidThermodynamicEntry values was drawn from – deliberately separate from CompetingPhase (reaction.rs), which stays exactly as it was in v0.3.0: adding these fields to that existing type would have changed its meaning from “a simple competing-candidate context note” to a data-provenance-tracking type, a caller-facing contract change advisor recommended against. Every field is caller-supplied, free text – gugen never fetches or validates dataset identity itself (AGENTS.md §8/§25, same “gugen never queries an external API” discipline materials_project_adapter.rs documents for CompetingPhase).
ThermodynamicSelectivityAssessment
One target’s finite-temperature thermodynamic picture at a given temperature – raw physical quantities only, structurally mirroring RouteSuitabilityAssessment’s own shape (Phase 15A): a vessel of independent, never-force-merged results plus its own limitations, not a verdict. Unlike RouteSuitabilityAssessment’s Supports/Contradicts/Unknown findings, nothing here is a judgment – every field is a number this module actually computed, or None where it couldn’t. Never read by score_plan: thermodynamic_support stays None regardless of what this type holds (checked as a permanent regression guard by tests/thermodynamics_ranking_invariance.rs, not left as an unverified claim). decomposition_comparisons is a Vec for the same reason RouteSuitabilityAssessment.findings is: a caller may name more than one alternative assemblage, and none should be silently dropped or merged into an aggregate.
ThermodynamicStabilityGenerator
Ranks caller-supplied precursor candidates by absolute thermodynamic stability – most-negative 0 K formation enthalpy per atom first, the standard materials-informatics cross-compound stability proxy (a convex-hull y-axis value). Never computed or fetched by this crate itself, matching the established “caller supplies real data, core never bundles it” convention (ThermodynamicProvider/ MaterialsProjectSnapshotProvider’s own precedent, and FrequencyPriorGenerator’s own shape).
UnresolvedCommercialField
Serialize only – see CommercialOfferSelection’s doc comment (field itself is &'static str).
UnresolvedRequirement

Enums§

ActualStepDetail
Mirrors process::ProcessStep’s variants and field names – reusing process’s own method/purpose enums directly, so “what was actually done” is drawn from the same closed vocabulary as “what was planned,” and a deviation is a straightforward field-by-field diff – but with point-value Option<f64> measurements instead of process’s validated min<=max range types: a range describes a planned target window, a real measurement is one number.
ApplicabilityLevel
Whether this planner can meaningfully handle the target at all (AGENTS.md §16). Distinct from per-plan confidence: applicability is about domain fit, not about how well-evidenced any particular plan is.
Atmosphere
AGENTS.md §12: atmosphere is never a bare string. v0.1 does not predict precise oxygen partial pressure; these variants are a formal oxidation-state / atmosphere-compatibility heuristic, not a guarantee of real phase equilibrium.
AvailabilityStatus
CharacterizationMethod
AGENTS.md §11 names XRD explicitly (“XRD等による中間確認” – XRD etc.); v0.1 only needs the one it names.
CommercialCatalogError
CommercialCatalogLoadMode
CommercialExclusionCode
CommercialRankingPolicy
Named procurement-combination ranking policies (Phase 24C), selected via assess_commercial_precursors_with_policy/assess_commercial_plans_with_policy. The plain assess_commercial_precursors/assess_commercial_plans always use Balanced, unchanged from their pre-24C behavior.
CommercialSourceType
How an offer’s data entered gugen – describes the ingestion mechanism, not anything about the product itself.
CoolingMode
CrossDoiFieldStatus
One field’s cross-DOI comparison result within one StepGroupKey. See literature_observation_conflicts’s module doc comment for the full rationale, especially why InsufficientIndependentSources and Unresolved are distinct (exactly one DOI reported this field vs. zero did) and why a lone value never resolves the field here, unlike Phase 19’s apply_condition_precedents – that function answers “do we have any data to fill this slot,” this one answers “do independent replications agree,” so a single source is insufficient by design, not an oversight.
DeviationCategory
A closed set of deviation kinds for querying, plus free text for nuance – mirrors PlanningWarning { message, severity }’s own message-plus-category shape.
EvidenceKind
AGENTS.md §7: every proposal must point at why it’s there, and the “why” is one of a closed set of kinds. If nothing here applies, the evidence kind is RuleBased with source_id: None – stated explicitly, never a gap left implicit.
EvidenceScope
How directly a piece of evidence applies to the plan it’s attached to, versus having been generalized from a related case (AGENTS.md §7’s SimilarComposition/SimilarStructure evidence kinds exist precisely because that distinction matters and must not be hidden).
EvidenceStrength
Categorical strength of one piece of evidence. Distinct from PlanScoreBreakdown.evidence_strength: Score01 (AGENTS.md §13), which is a plan-level aggregate across many evidence items with a weighted, documented rationale – that aggregation is Phase 5 work. This type must not grow an unstated enum-to-number mapping before then.
ExecutionRecordLoadMode
Whether a malformed or schema-mismatched line aborts the whole parse (Strict) or is skipped and reported (Lenient) – mirrors commercial_catalog::CommercialCatalogLoadMode’s identical two-mode precedent.
FormingMethod
GrammarEvidenceClass
How certain a proposal’s own arithmetic is, never a claim about whether the reaction actually occurs. Declaration order is significant: variants are ordered most- to least-certain, and propose_all keeps the most-certain evidence class when the same composition is proposed by more than one grammar. #[non_exhaustive] because a third category (Speculative, for a future looser grammar) is already anticipated but not needed for this PR’s four grammars, which are either exact charge/mass-balance derivations (Stoichiometric) or a single empirically-motivated heuristic (CommonDecompositionHeuristic).
GrindingMethod
GugenError
Crate-wide error type. Construction of any public numeric or compositional type goes through validation that returns this error instead of panicking (AGENTS.md §25: “panicを通常入力の処理に使用しない”).
HeatingPurpose
Calcination/sintering/annealing map directly onto AGENTS.md §11’s own 仮焼/本焼成 outline.
InertGas
Not given verbatim by AGENTS.md §12 – kept to the two gases a v0.1 atmosphere heuristic actually needs to name. Add a variant only when a real curated fixture (Phase 8) needs one this doesn’t cover. #[non_exhaustive] (v0.5.0, Phase 23D) since this doc comment already states the growth expectation.
LoadMode
Whether LiteratureObservationCorpus::load fails the entire load on the first malformed observation (Strict), or skips and counts malformed observations while still returning every observation that did parse (Lenient). Manifest-level problems (schema version, record-count mismatch) are hard failures in either mode – they mean “this is not a snapshot this gugen version can read at all,” not “this snapshot has some noisy rows.”
MissingCommercialDataPolicy
MixingMethod
Method/purpose sub-enums ProcessStep references (AGENTS.md §6). Not given verbatim – each is kept to the standard techniques the §11 template outline itself needs, not an exhaustive taxonomy. Add a variant only when a real curated fixture (Phase 8) needs one this doesn’t cover. #[non_exhaustive] on all three (v0.5.0, Phase 23D) since this doc comment already states the growth expectation for all three.
ProcessStep
AGENTS.md §6’s ProcessStep. Step meaning is kept as structured data; only final display is stringified.
ProviderError
Error returned by external data providers (AGENTS.md §8). Deliberately separate from GugenError: one provider failing must not by itself fail planning that doesn’t depend on it (AGENTS.md §21.5).
ReducingAgent
RejectionCode
Closed set of rejection reasons (AGENTS.md §14). ThermodynamicDataUnavailable must not by itself force a reject — callers may downgrade it to a warning or lowered confidence instead (AGENTS.md §13).
RouteError
Failure modes specific to assembling/validating a SynthesisRoute. Deliberately a separate enum from GugenError, not a new variant on it – GugenError is a public, non-#[non_exhaustive] enum, so adding a variant there would be a breaking change for every downstream exhaustive match. ProviderError already establishes the “a distinct concern gets its own error enum” precedent in this crate (src/error.rs); route assembly is similarly distinct from core numeric/compositional validation.
RouteFamily
AGENTS.md §13. v0.1 shipped with exactly one route family; add a new variant only with real literature grounding for its process structure (never invented from memory, AGENTS.md §21.3), following the process used for Mechanochemical (Phase 12) – not pre-added speculatively. #[non_exhaustive] (v0.5.0, Phase 23D) since this doc comment already states the growth expectation; matches this crate’s existing SuitabilityVerdict/RouteRecommendation precedent.
RouteRecommendation
Discrete recommendation derived from a RouteSuitabilityAssessment (Phase 15B) – never a numeric score. #[non_exhaustive], matching SuitabilityVerdict’s precedent. Recommended carries no ranking weight in this phase: nothing in score.rs reads it, and only NotRecommended has a real behavioral effect (Planner::plan excludes that plan from the recommended list – see derive_recommendation).
StepRequirement
AGENTS.md §11: every step must declare how firmly it applies. A step with unknown conditions is kept as Unresolved, never silently dropped.
SuitabilityVerdict
AGENTS.md §4.3: thermodynamic favorability alone must not be read as experimental likelihood. This type extends the same separation to route-family choice – a Contradicts verdict is one specific, sourced reason to question a route, never a computed probability. #[non_exhaustive] since this is a genuinely new, still-evolving vocabulary (Phase 15A) – none of gugen’s other public enums have this guard, but this one is deliberately started with it rather than retrofitted later.
SynthesisOutcome
A 7-state outcome, not success/failure (the owner’s explicit requirement) – NotMeasured is itself a distinct, honest state, not the same as omitting the field.
TieBreakPolicy
Diagnostic-only, public tie-break selector (Phase 30.5, search_diagnostics feature) for search_precursor_sets_diagnostic. Never accepted by search_precursor_sets itself, which always behaves exactly as it did before this type existed.
WarningSeverity

Constants§

CORPUS_SNAPSHOT_SCHEMA_VERSION
gugen’s own snapshot schema identifier, unrelated to the source corpus’s own versioning. LiteratureObservationCorpus::load rejects a snapshot whose manifest.schema_version does not match this exactly – there is no cross-version compatibility logic, deliberately: a schema change is a breaking change to what this module can parse, not something to silently paper over.
ELEMENT_SYMBOLS
The 118 IUPAC element symbols, used to validate that a symbol supplied by a caller is a real element rather than a typo. This is chemical-notation fact, not a synthesis claim, so it is safe to hold as a static table (contrast with AGENTS.md §4.1’s rule against unsourced temperature/time values, which does not apply here).
EXECUTION_RECORD_SCHEMA_VERSION
Namespaced and independently versioned – deliberately distinct from report::SCHEMA_VERSION, which docs/api_stability_policy.md documents as not a strict shape guarantee for exactly this reason: a SynthesisExecutionRecord is a long-lived, externally-persisted artifact accumulated across gugen versions over months, unlike a SynthesisPlanningReport generated fresh each run. Matches literature_observations.rs’s own CORPUS_SNAPSHOT_SCHEMA_VERSION precedent for this exact kind of artifact.
SCHEMA_VERSION
Bumped whenever SynthesisPlan’s or SynthesisPlanningReport’s JSON shape changes in a way a strict deserializer (e.g. one rejecting unknown fields) would need to account for. 2 as of v0.4.0: adds SynthesisPlan.literature_evidence, confirmed as a breaking addition by cargo semver-checks’s constructible_struct_adds_field lint – see docs/literature_evidence_integration.md. 3 as of Phase 26: adds SynthesisPlan.prior_experiment_evidence, the identical breaking class – see docs/prior_experiment_evidence.md.

Traits§

CandidateGenerator
One source in a multi-source candidate-generation ensemble (Phase 30, PR 1: catalog-exact and frequency-prior; later PRs add thermodynamic, prior-experiment, and literature-analog). Deliberately narrower a contract than PrecursorCatalog: PrecursorCatalog answers “what’s in the catalog for this target,” unranked; CandidateGenerator answers “what does this specific signal propose, in its own preferred order” – GeneratedCandidate::rank carries that order as a plain ordinal, never a score, so a generator’s internal priority can never be read as a success probability (mirrors SearchPriority‘s own score/priority separation, src/precursor.rs). CandidateGeneratorEnsemble (src/candidate_generator.rs) is the only place multiple generators’ outputs are combined, and it does so by implementing PrecursorCatalog itself – so an ensemble is a drop-in Planner::builder catalog argument, requiring no change to Planner/ PlannerBuilder at all.
LiteratureEvidenceProvider
Source of reference-only, cross-DOI literature evidence for an exact target/precursor-set/route-family combination (v0.4.0 Integration). Deliberately narrower in spirit than ProcessEvidenceProvider: this trait’s output (LiteratureRouteEvidence) is never applied to a ProcessStep, never converted to a ConditionPrecedent, and never passed to score_planPlanner attaches it to SynthesisPlan as its own field, structurally isolated from every scoring input. Not gated behind the literature_corpus feature: this trait and LiteratureRouteEvidence are always compiled, so Planner’s public API and report schema never change shape depending on which crate features are enabled. The one real implementation (LiteratureObservationCorpusProvider, backed by LiteratureObservationCorpus::cross_doi_comparisons) lives behind that feature – same split ThermodynamicProvider already has against MaterialsProjectSnapshotProvider.
PrecursorCatalog
Source of candidate precursor compounds for a target (AGENTS.md §8). Core ships in-memory/JSON/fixture implementations only — no network access lives in this crate (AGENTS.md §8, §25).
PriorExperimentEvidenceProvider
Source of reference-only prior-experiment evidence for an exact target/precursor-set/route-family combination (Phase 26). Mirrors LiteratureEvidenceProvider’s own exact-match contract exactly: this trait’s output (PriorExperimentEvidence) is never applied to a ProcessStep, never converted to a ConditionPrecedent, and never passed to score_planPlanner attaches it to SynthesisPlan as its own field, structurally isolated from every scoring input. Not gated behind any Cargo feature: unlike LiteratureEvidenceProvider (whose one real implementation needs the literature_corpus feature’s corpus loader), crate::execution_record::SynthesisExecutionRecord itself carries no feature gate, so neither does the one real implementation of this trait (crate::prior_experiment_evidence::InMemoryExecutionRecordProvider). Ok(Some(evidence)) implies evidence.records is non-empty – an empty match is Ok(None), never a rendered “0 prior experiments” disclosure. Unlike LiteratureEvidenceProvider, Planner does not restrict which route families this provider is asked about (see docs/prior_experiment_evidence.md for why that restriction doesn’t apply here).
ProcessEvidenceProvider
Source of process-condition precedent for a target/precursor combination (AGENTS.md §8).
RouteSuitabilityProvider
Source of route-suitability findings for a target/route-family combination (Phase 15A; AGENTS.md §8 sketches providers as a floor, not an exhaustive list – Phase 13’s competing_phases already extended this pattern once). Deliberately narrower than ProcessEvidenceProvider: suitability depends only on the target and route family, not on a specific precursor set, since a route family’s fitness for a material (e.g. whether high-temperature firing is even reachable before the target decomposes) doesn’t change with which precursors were chosen to reach it.
TargetMaterialView
Boundary trait gugen depends on instead of a concrete crystal-structure type (AGENTS.md §5). Implemented directly by TargetSpecification today; a future chematic-crystal adapter implements it over the real structure types without changing anything downstream of target.rs (docs/integration.md).
ThermodynamicProvider
Source of reaction-energy estimates (AGENTS.md §8). Returning Ok(None) means “no data available,” which must not by itself reject a plan (AGENTS.md §13, §14 — see RejectionCode::ThermodynamicDataUnavailable).
TransformationGrammar
A transformation grammar: given the real precursor compositions available for a synthesis, proposes candidate intermediate compositions via one fixed, explainable mechanism. Implementations must never assert that a proposal is a real reaction product – only search_precursor_sets’s own balance() check, applied downstream, decides that.

Functions§

applicable_route_family_templates
Every route family gugen currently supports, applied unconditionally to the same accepted precursor set (Phase 12). One accepted precursor set now generally becomes more than one ranked SynthesisPlan – callers (Planner::plan) turn each returned ProcessTemplateResult into its own plan, rather than assuming exactly one template per accepted set.
assess_commercial_plans
Maps assess_commercial_precursors over each plan independently (fresh max_combinations_evaluated budget per plan). Err is reserved for a self-contradictory request – checked once, up front, since it is identical for every plan in the batch; a single malformed plan never aborts the batch (see assess_commercial_precursors’s degraded-Ok handling for plan-shape issues). Always ranks via CommercialRankingPolicy::Balanced – unchanged from this function’s pre-24C behavior. See assess_commercial_plans_with_policy for named-policy selection.
assess_commercial_plans_with_policy
assess_commercial_precursors
Always ranks via CommercialRankingPolicy::Balanced – unchanged from this function’s pre-24C behavior. See assess_commercial_precursors_with_policy for named-policy selection.
assess_commercial_precursors_with_policy
balance
Solves for integer, gcd-normalized coefficients balancing reactants against products (AGENTS.md §10), using exact-rational Gauss-Jordan elimination over the element x species matrix — never floating-point approximation.
balanced_reaction_delta_ev_per_atom
Products’ total relative_solid_gibbs minus reactants’ total, weighted by each species’ ReactionSpecies::coefficient and formula-unit atom count, normalized per atom of the reactant side (mirrors MaterialsProjectSnapshotProvider::reaction_energy’s exact convention and doc comment for why the reactant side is used).
conventional_solid_state_template
AGENTS.md §11’s conventional solid-state route: weigh, mix, grind, optionally form, calcine (only if the balanced reaction shows a byproduct that needs decomposing off), sinter, cool, and characterize.
curated_byproducts
Curated allow-list of byproduct compositions v0.1 permits assuming without target-specific evidence (AGENTS.md §10). A reaction that needs a byproduct outside this list is RejectionCode::UnsupportedByproductRequired, not a silently invented one.
decomposition_margin_ev_per_atom
The energy margin between target and one specific, caller-named alternative combination of phases covering the exact same total elemental composition – e.g. “does BaTiO3 have lower Gibbs energy than BaO + TiO2”. Deliberately not a hull search or an automatic “find the best decomposition” function (the owner’s original Phase 19P sketch asked for a “small binary/ternary hull”; this is a considered substitution, not a silent scope cut – see this phase’s work report). A hull search would require gugen itself to decide which candidate assemblages to enumerate from entries, so a returned margin would only ever mean “no cheaper decomposition among the phases this particular caller happened to supply,” not “the target is thermodynamically stable” – a reader could easily misread the former as the latter, which is exactly the false-confidence risk the owner’s own stop-and-report list names. Requiring the caller to name the specific alternative assemblage keeps the claim exactly as narrow as what was actually computed.
default_grammars
Every grammar shipped in this PR, in a fixed order (used by propose_all’s callers, e.g. the PR 3 benchmark harness, so “grammar -only” runs are reproducible without hand-listing grammars at each call site).
derive_recommendation
Pure function: no Planner/provider dependency, safe to unit-test directly against a hand-built RouteSuitabilityAssessment. Order- and duplicate-invariant by construction (any/iter over the whole findings slice, not the first match).
literature_evidence_limitations
A fixed baseline of disclosures every LiteratureRouteEvidence carries (Phase 20D’s own findings about what this corpus can and cannot certify), plus conditional additions when assessment itself shows step-count diversity or a field-level conflict. Deliberately never phrases “more evidence surfaced” as “conditions are more accurate” – this reports what was found, not a confidence claim.
mechanochemical_template
Phase 12’s second route family: weigh, then high-energy ball milling (GrindingMethod::BallMilling) as one combined mixing-and-grinding operation, optionally consolidated (pressed) into a bulk shape, with a post-milling anneal only when the balanced reaction needs one.
parse_execution_records
Parses a JSONL document (one SynthesisExecutionRecord per non-blank line) – pure, no file I/O (see the module doc comment). Every line’s schema_version is checked against EXECUTION_RECORD_SCHEMA_VERSION here, per line rather than via a single whole-file header: a real execution log accumulates appends across gugen versions over months, so no one file-wide version gate fits its lifecycle, and a header line would itself be a mutation hazard for an append-only file under concurrent or crash-prone writers that per-line self-description avoids entirely.
propose_all
Runs every grammar in grammars against precursors, caps each grammar’s own raw output at per_grammar_cap, then deduplicates identical compositions across grammars – retaining every contributing grammar’s id and the most-certain evidence class seen – sorted most-certain/most-corroborated first and capped combined at combined_cap. Mirrors CandidateGeneratorEnsemble’s own per-source-cap-then-merge shape (src/candidate_generator.rs).
ranking_weights_digest
A deterministic fingerprint of weights, for PlanningProvenance.ranking_config_digest (AGENTS.md §13: “weightをprovenanceに保存”). Not a cryptographic hash – DefaultHasher is enough to detect “did the ranking config change between two runs”, which is all provenance needs it for.
reduced_mass_amu
Bartel et al. 2018 Eq. 6: a composition-ratio-weighted pairwise combination of atomic masses. None for a single-element composition (no pairs exist – callers must special-case pure elements the same way pymatgen’s gf_sisso does, returning a zero correction, since a pure element’s formation enthalpy is 0 by definition and has no SISSO correction to add).
relative_solid_gibbs_ev_per_atom
formation_enthalpy_ev_per_atom + Gδ(T) – a finite-temperature formation-Gibbs-energy estimate relative to the same 0 K elemental reference frame formation_enthalpy_ev_per_atom already uses, not an absolute value (this module never computes or needs the elemental re-basing term – see the module doc comment). Meaningful only in differences between entries sharing the same total elemental inventory (a balanced reaction, or a decomposition margin) – see balanced_reaction_delta_ev_per_atom.
score_plan
Scores one plan’s ingredients (AGENTS.md §13/§16). route_family (Phase 12) only affects process_simplicity, via step_bounds – every other dimension is computed the same way regardless of route family.
search_precursor_sets
Searches for precursor sets that can plausibly produce target, drawn from candidates (typically the output of a PrecursorCatalog query), respecting constraints and budget.
search_precursor_sets_diagnostic
Diagnostic-only (Phase 30.5, search_diagnostics feature): runs the exact same frontier mechanism search_precursor_sets uses (search_precursor_sets_core, shared, unchanged pruning/budget/dedup rules), under a caller-selected TieBreakPolicy, tracing only what this phase’s pre-registered questions need about one specific known-correct (“gold”) precursor set.
search_two_step_routes
Searches for routes to target of depth 1 (today’s existing search_precursor_sets, unchanged) and depth 2 (via each composition in intermediate_candidates). intermediate_candidates is caller-supplied, never computed or fetched by this function – matching FrequencyPriorGenerator’s own “caller-supplied, never computed by the crate” convention (src/candidate_generator.rs); a caller with a ThermodynamicProvider can source it from competing_phases(target), but this function stays provider-agnostic and trivially testable with plain fixtures.
structural_effects
Maps one mikiwame::MaterialDiagnosticReport to gugen-native effects.
to_mikiwame_structure
Converts a chematic-crystal structure into a mikiwame one. See the module doc comment for why this isn’t a plain field-by-field copy.

Type Aliases§

Result