Audit a Real SynPlanner Route with RENKIN
SynPlanner has a real native route export. RENKIN audits it directly.
SynPlanner
ships its own real route-export function
(synplan.chem.reaction.routes.write_routes_json). RENKIN Bridge consumes
that export directly — no custom exporter package needed, unlike Syntheseus
— through the exact same tool-neutral pipeline RENKIN's own routes and
AiZynthFinder's routes already go through: structural integrity, stock, and
declared-reaction forward-replay validation, each reported independently,
rolled up into a route-level pass/fail/partial verdict.
This page walks through it end to end against real SynPlanner 1.6.0 output — not a hand-authored example — so every command and every line of output below is something you can reproduce yourself from a checkout of this repo.
The fixtures
tests/fixtures/synplanner/v1.6.0/ has fixtures from two separate real
capture rounds — full provenance (exact package/checkpoint versions,
licenses, construction code, SHA-256 checksums) is in the two sibling
PROVENANCE.md files.
route_1_two_step.json/route_3_full_fields.json— Phase 0: realchythonreaction objects run through SynPlanner's own real exporter (write_routes_json), not hand-typed JSON.route_3_full_fields.json's chemistry is deliberately a toy substitution (its atom map reuses one number across an O→Cl identity change) to exercise every optional schema field at once — not a claim of real synthetic feasibility (seePROVENANCE.md).real_planning_route_1step.json/real_planning_route_2step.json— Phase 1 PR1.5: sliced verbatim from a genuine, CPU-only, 167-route MCTS search for aspirin, run through SynPlanner's realsynplan planningCLI end to end (real pretrainedsynplanner-gpscheckpoints, MIT-licensed).
This walkthrough uses the real-planning fixtures — they're the ones that demonstrate SynPlanner's standout property below.
Step 1: audit without a configured stock
renkin audit-route tests/fixtures/synplanner/v1.6.0/real_planning_route_1step.json \
--format synplanner \
--output human
stock: StockNotProvided is the same honest "we didn't check" result every
adapter reports without --stock — see the
AiZynthFinder demo for why that's correct,
not weaker. Notice what's not here: no forward: MissingAtomMapping, no
ForwardValidationNotEvaluable. That's not an omission — see Step 3.
Step 2: audit with a configured stock
renkin audit-route tests/fixtures/synplanner/v1.6.0/real_planning_route_1step.json \
--format synplanner \
--stock /tmp/synplanner_demo_stock.smi \
--output human
where /tmp/synplanner_demo_stock.smi contains the route's own two real
precursors:
A genuine PASS — not PARTIAL. This is the one adapter in RENKIN today
where that's possible on a real, unmodified export: the reaction's own
smiles field carries a real, usable atom map, so declared-reaction forward
replay actually runs and actually succeeds. Step 3 explains why.
Step 3: why forward validation genuinely passes here
RENKIN's declared-reaction-replay check needs an atom-mapped SMIRKS to know
which atom in the product came from which atom in the reactants.
AiZynthFinder's route metadata optionally carries one; Syntheseus's
reaction_smiles is a plain reactants>>product string with no mapping at
all on every real Syntheseus route today — both stay not_evaluable (see
the Syntheseus demo's own Step 3).
SynPlanner is different: its reaction node's smiles field is a real,
atom-mapped SMIRKS by construction, and — confirmed against this route's
real, CPU-only MCTS-searched output, not just a hand-built example — that
map is internally valid (no duplicate or orphan atom numbers) and consistent
across every step boundary in the route, even though SynPlanner's own export
code documents its default path as using non-cross-step-reconciled
numbering. renkin audit-route doesn't invent this evidence — it's simply
what SynPlanner's own exporter already writes; RENKIN just doesn't discard
it the way it has no choice but to for the other two adapters (see
docs/design/synplanner-adapter-v1.md §7 for the full audit trail, and
scripts/tests/test_synplanner_real_route_fixture.py /
src/bridge/synplanner.rs's own unit tests for the exact numbers: 317/317
reaction nodes with valid maps, 150/150 cross-step boundaries consistent,
across a real 167-route search).
This isn't a claim that every SynPlanner route always replays. It's
scoped to what was actually tested: one target (aspirin), one preset
(synplanner-gps), the exporter's default (non---reconcile-mapping)
path. A route whose declared reaction genuinely doesn't reproduce the target
still correctly reports fail, not a forced pass — see the next section.
A route that correctly fails
route_3_full_fields.json (Phase 0) is deliberately toy chemistry: its
SMIRKS reuses one atom-map number across an O→Cl identity change, which
isn't valid atom-mapped chemistry (the same map number is supposed to track
the same atom, and oxygen isn't chlorine).
renkin audit-route tests/fixtures/synplanner/v1.6.0/route_3_full_fields.json \
--format synplanner \
--output human
1 routes audited — 0 pass, 1 fail, 0 partial
route 1/1: FAIL
- UnaccountedTargetElement
- ForwardReactionNotReproduced
- stock: StockNotProvided
UnaccountedTargetElement catches the missing chlorine source (the product
has a Cl atom no declared precursor supplies), and the real replay engine
correctly refuses to reproduce the target from this transformation. This is
the honest complement to Step 2's PASS: RENKIN's forward validation is
running real chemistry against SynPlanner's real declared reactions either
way, not pattern-matching structure alone — a route with real, valid
chemistry passes, and a route without it fails, on the same adapter, same
code path, no special-casing either direction.
What --format auto does here
Every command above passes --format synplanner explicitly. Omit it and
--format auto (the default) detects the same result on its own, by
looking for a top-level object whose keys all parse as non-negative
integers (route IDs) and whose values are themselves objects with
"type": "mol" at their root — checked ahead of RENKIN-native's own
{"target": ..., "routes": [...]} shape and AiZynthFinder's batch
{"schema": ..., "data": ...} shape, since all are top-level objects and
this is the more specific signal.
Not yet supported: the separate --export_routes "public contract"
wrapper format SynPlanner's CLI can also emit (manifest.json +
results.json.gz, a target-SMILES-keyed list rather than a route-ID-keyed
object). Only the {route_id: RouteNode} shape every fixture above uses is
recognized today — a deliberate, tracked scope boundary, not a silent gap
(see docs/design/synplanner-adapter-v1.md).
Compatibility
| Source | Verified version | Input |
|---|---|---|
| SynPlanner | 1.6.0 | write_routes_json's {route_id: RouteNode} export only — not the separate --export_routes wrapper format |
See the AiZynthFinder demo and Syntheseus demo for those adapters' own compatibility rows.
Compatibility-verified and forward-validation-capable are two different
claims — and unlike the other two adapters, both happen to hold here.
Confirmed for real, CPU-only MCTS-searched output from one target
(aspirin) and one preset (synplanner-gps); not yet re-verified against
other presets, --reconcile-mapping, or a batch/multi-target search. See
the Audit Reproducibility and Compatibility Contract
for what "verified" means across every adapter in general.
Hit a compatibility problem?
If audit-route --format synplanner rejects real SynPlanner output, or a
real route gets flagged not_evaluable/fail in a way that looks wrong,
please
open an adapter-compatibility issue —
it's set up to capture exactly what turns a real report into the next test
fixture.