NatLabRockies / NatLabRockies/r2x-cli

fix(cli): pass durable System to context-based class exporters

Open
#168 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug rust
Dominant language
Rust
Stars
4
Forks
2
Avg merge
1d 4h
Merged PRs (30d)
12

Description

Summary

r2x run <exporter> -i <system.json> rejects a context-based class exporter even after the bridge has loaded the System into PluginContext.system.

This blocks the public durable-boundary contract for r2x-plexos.plexos-exporter.

Environment

  • r2x-cli 0.1.8 (0d384a15cb96c360afb1ff8cc126263f6021d02f)
  • r2x-plexos 0.3.0 (51a93284ddeffa3a0964c780cd3c5565b9bd6e8f)
  • r2x sync completed successfully and does not change the result.

Reproducer

After creating a valid durable translated System with the public CLI:

r2x run r2x-plexos.plexos-exporter \
  -i /tmp/geothermal-r2x-018-short-smoke/translated-system.json \
  --output-path /tmp/geothermal-exporter-repro \
  --model-name Geothermal_A1 \
  --horizon-year 2050 \
  --weather-year 2012 \
  --template PLEXOS10.0

Verbose output shows the contradictory dispatch:

DEBUG: Invoking plugin with target: r2x_plexos:PLEXOSExporter.export
DEBUG: Parsing target: r2x_plexos:PLEXOSExporter.export
DEBUG: Using method 'run' for plugin 'PLEXOSExporter'
Error: Run command failed: Python bridge error: Plugin stream error: plugin method 'PLEXOSExporter.run' does not accept a System or JSON input

The command then aborts with the known GIL finalization failure.

Why this is incorrect

PLEXOSExporter is a context-based r2x_core.Plugin:

  • It implements on_export(self) and obtains the model through self.system.
  • self.system reads PluginContext.system.
  • It inherits Plugin.run(self, *, ctx=None); it intentionally accepts no positional System argument.

The bridge already deserializes the -i artifact and passes it into PluginContext.system for the exporter role. It then chooses inherited run() and applies the generic positional-System signature check, rejecting the input before run() can execute on_export().

Additionally, the role-derived PLEXOSExporter.export call target is synthetic: this exporter defines no concrete export() method.

Expected behavior

For a context-based exporter:

  1. Deserialize -i into PluginContext.system.
  2. Construct the plugin from that context.
  3. Invoke its lifecycle run() with no positional payload.
  4. Treat the exporter as a terminal sink.

Suggested fix

In the class-plugin bridge, exempt context-based exporter lifecycle invocation from the generic System/JSON positional-argument validation. Once the bridge has populated PluginContext.system, call run() with no positional argument.

Avoid treating a role-derived Class.export string as a concrete class method unless the plugin defines that method. An adapter convention is also viable, but it should preserve the documented r2x run <exporter> -i <system.json> contract.

Regression coverage

Add an integration fixture that:

  • defines a class exporter inheriting Plugin, with on_export() reading self.system and no concrete export() or run() override;
  • invokes it through direct short-form r2x run <plugin-ref> -i <durable-system.json>;
  • verifies the exporter receives the System and writes its configured terminal artifact;
  • verifies no JSON is emitted to stdout.

This should cover real r2x-plexos exporter lifecycle semantics without requiring PLEXOS in the CLI test suite.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the class-plugin bridge used by the direct short-form r2x run <plugin-ref> -i <durable-system.json> entry point, then reproduce the exporter failure described above. Add the integration fixture with a Plugin exporter whose on_export() reads self.system and has no concrete export() or run() override. Done means the System reaches the exporter, its terminal artifact is written, and no JSON is emitted to stdout.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
backend, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.