Common BanterLab plans

Three end-to-end recipes the LLM (or you, via the OPAL chat overlay) can run against an open BanterLab session. Every step here is one OPAL Task — copy the JSON blocks straight into the chat. The full OT reference lives at BanterLab OPAL reference.

Prerequisites: open the BanterLab activity in hub at least once so the locator can find it. All OTs fail with a BanterLab is not open message until that happens.


1. Build and persist a test set

Goal: compose a 3-test set in memory, save it to disk, and walk away. Useful when you want to script up a campaign and run it later (or hand the JSON file to a colleague).

{ "task": "banter.testset.new",          "args": {"name": "agc-sweep", "description": "AGC characterization across modes"} }

Add the two nodes the run will use. bus/bus keeps everything in-process; pass real device names (from banter.audio.list-devices) for over-the-air runs.

{ "task": "banter.node.add", "args": {"name": "Alice", "output-device": "bus", "input-device": "off"} }
{ "task": "banter.node.add", "args": {"name": "Bob",   "output-device": "off", "input-device": "bus"} }

Set test-set-wide defaults so each test inherits payload, codec, and node routing without repeating itself.

{ "task": "banter.testset.set-defaults", "args": {
    "codec": "FSK",
    "payload": "HELLO",
    "iterations": 30,
    "nodes": ["Alice", "Bob"]
} }

Append three tests. Each only specifies what differs from the defaults — BanterTestSet::resolveTest merges them at run time.

{ "task": "banter.testset.add-test", "args": {"name": "agc-off",        "parameters": [{"id": "agcMode", "current": 0}]} }
{ "task": "banter.testset.add-test", "args": {"name": "agc-normal",     "parameters": [{"id": "agcMode", "current": 1}]} }
{ "task": "banter.testset.add-test", "args": {"name": "agc-freeze",     "parameters": [{"id": "agcMode", "current": 2}]} }

Persist to disk; the path becomes the test set's tracked source so future banter.testset.save calls overwrite the same file.

{ "task": "banter.testset.export", "args": {"path": "/tmp/agc-sweep.json"} }

Later (after edits), re-save with one call:

{ "task": "banter.testset.save" }

2. Run an RL learning sweep over baud rate

Goal: let the lab tune fsk_baudRate in the [50, 600] range to maximize a reward driver that combines decode success and SNR. The bandit explores values and records improvements in a .rlpol sidecar.

Mark the parameter as learning (the sweep mode that hands control to the RL controller).

{ "task": "banter.param.set-sweep", "args": {
    "id": "fsk_baudRate", "mode": "learning", "min": 50, "max": 600
} }

Configure the RL controller. simple-bandit works fine with 5–50 episodes; switch to ppo for heavier setups.

{ "task": "banter.policy.set-algorithm",     "args": {"algorithm": "simple-bandit"} }
{ "task": "banter.policy.set-reward-driver", "args": {"driver": "decode-and-snr"} }

Set up a one-test set so the run is bounded.

{ "task": "banter.testset.new",        "args": {"name": "learn-baud"} }
{ "task": "banter.testset.set-defaults", "args": {"codec": "FSK", "payload": "HELLO", "iterations": 30, "nodes": ["Alice", "Bob"]} }
{ "task": "banter.testset.add-test",   "args": {"name": "sweep"} }
{ "task": "banter.testset.export",     "args": {"path": "/tmp/learn-baud.json"} }

Kick the run. start-testset is long-running — the OT reports per-iteration progress and respects cancellation.

{ "task": "banter.run.start-testset" }

Once it finishes, snapshot the trained policy alongside the test set:

{ "task": "banter.policy.save" }

Reload it on the next session before re-running:

{ "task": "banter.policy.load" }

The sidecar's stored fingerprint is checked on load — if you've changed which params are in learning mode (or their bounds) since the save, policy.load fails with the stored vs expected fingerprint and the suggestion to retrain.


3. Compare two codecs side-by-side

Goal: run the same payload across FSK and OFDM in one test set, then query the results database to compare scores.

{ "task": "banter.testset.new",          "args": {"name": "fsk-vs-ofdm"} }
{ "task": "banter.testset.set-defaults", "args": {"payload": "HELLO", "iterations": 50, "nodes": ["Alice", "Bob"]} }
{ "task": "banter.testset.add-test",     "args": {"name": "fsk",  "codec": "FSK"} }
{ "task": "banter.testset.add-test",     "args": {"name": "ofdm", "codec": "OFDM"} }
{ "task": "banter.testset.export",       "args": {"path": "/tmp/fsk-vs-ofdm.json"} }
{ "task": "banter.run.start-testset" }

After completion, pull the most recent rows from the SQLite results database. The default limit is 100 rows, sorted by id descending — most-recent runs first.

{ "task": "banter.results.query", "args": {"limit": 200} }

The returned rows JSON array carries one entry per iteration with score, codec, params, and timestamps. The LLM can summarize that arbitrarily — typical shape is "FSK averaged 0.87 decode rate, OFDM averaged 0.74 over the same payload".


Cancelling and skipping

Both banter.run.start-single and banter.run.start-testset honour the OPAL runtime's cancellation. From a separate task:

{ "task": "banter.run.abort" }

Mid-test-set, you can also drop the current test and continue with the next one:

{ "task": "banter.run.skip-test" }

abort is idempotent — calling it with no run active is a benign success.


Troubleshooting

Symptom Likely cause Fix
Failed to ...: BanterLab is not open Activity not yet mounted Open the BanterLab activity in hub once; the locator's children persist after that.
Failed to set fsk_baudRate: parameter scoped to codec FSK but current codec is OFDM Codec scope guard Call banter.codec.set with codec=FSK first.
Failed to save: test set has not been imported or exported yet mCurrentTestSetPath empty Call banter.testset.export path=... once; subsequent save calls reuse that path.
Failed to load policy: fingerprint mismatch (...) Learning param action space changed since save Either revert the learning params, or accept retraining with a fresh policy.
Failed to calibrate: requires at least 2 nodes Fewer than two nodes banter.node.add until the bus has Alice + Bob.

For the full failure-message catalogue see the reference's Failure messages section.


What's next