All plugins

gonogo

Turn a pilot-scale eval score into a deployment decision — Wilson intervals, a confidence threshold that routes uncertain cases to a human, and an honest INSUFFICIENT EVIDENCE verdict when the sample cannot support the claim. Disclosure — gonogo_report writes report JSON/HTML to the path the model supplies.

keppyToolsv0.3.0Updated 12 days ago

About

Turn a pilot-scale eval score into a deployment decision — Wilson intervals, a confidence threshold that routes uncertain cases to a human, and an honest INSUFFICIENT EVIDENCE verdict when the sample cannot support the claim. Disclosure — gonogo_report writes report JSON/HTML to the path the model supplies.

gonogo — a Hermes plugin

47 out of 50 is not 94%. It lands somewhere between 84% and 98%, so if you
were aiming at 95%, you cannot yet say you got there.

This plugin gives a Hermes agent a decision instead of a number. Ask it whether
something is good enough to ship and it answers with one of five verdicts, the
interval behind it, the confidence threshold that routes uncertain cases to a
human, and — when the sample is simply too small to say — that it is too small
to say.

hermes plugins install keppy/hermes-plugin-gonogo
hermes plugins enable gonogo

Requires gonogo-eval>=0.4,<0.5, which Hermes installs for you from the
plugin's pyproject.toml. Expected actions (a case whose right move is to
wait, ask or not reply) and the borderline compare verdict arrived in 0.4.

The ecosystem this belongs to

This plugin is the in-session half of a small ecosystem that takes a task
from which model? to ship it or not:

your task ──► evalroute ─ choose a (model, effort) arm;
                │         measured where available, priors marked
                ▼
your cases ──► thomas ── train on labels (encoder) or score_text (RL);
                │         evaluate on untouched cases with gonogo
                ▼
              gonogo ── ship it, test a threshold on fresh cases, or walk away
  • gonogo — the library behind this
    plugin, and the root of the map. Any agent, your real cases, a target;
    the verdict comes with the interval behind it.
  • thomas — the training harness. RL
    shares score_text across training and held-out evaluation; encoder SFT
    instead learns labels and needs a separate held-out evaluation.
  • evalroute — the
    routing layer: classify the task, hand back the arm with measured
    costs where available and priors otherwise; collect ratings to prioritize
    the next controlled batch.
  • The Hermes plugins — the same three, inside your agent's session:
    this one for decisions, thomas
    with a human approval hook on its training tool, and evalroute's /route
    before the first turn.

What it adds

| Tool | The question it answers |
| --- | --- |
| gonogo_decide | "Is this good enough to ship?" — from a pass count or a results file |
| gonogo_report | "Give me the report artifact" — for a doc, a PR, a pilot review |
| gonogo_compare | "Is B actually better than A?" — McNemar on paired cases |

Plus a bundled skill, gonogo-verdict, that loads the reporting discipline
with it: quote the interval and not the point estimate, read the verdict off
the lower bound, keep the calibration caveat, keep the self-implicating
detail in.

Why a plugin and not a library

The library already exists — pip install gonogo-eval. What a plugin changes is
who asks. Any Hermes session can now end a measurement with a defensible call
instead of a plausible-looking percentage, in the same conversation where the
measurement happened, without anyone remembering to go run a script.

A worked exchange

You: The invoice router got 47 of 50 right. Ship it?

Agent: (calls gonogo_decide with passed=47, total=50, target=0.95)

**ASSIST ONLY** — pass rate 94.0% [83.8%, 97.9%] is below the 95% target
and no confident subset reaches it; useful as a draft-generator, not as an
unattended step

Pass rate 94.0% [83.8%, 97.9%] over 50 cases (47 passed), target 95%
Note: Every case reported the same confidence, so no abstention threshold can be
derived. Emit a real per-case confidence to unlock selective automation.

Two things that matter there. The interval reaches down to 83.8%, so 94% is not
the number the decision gets made on. And the tool noticed the results carried no
per-case confidence — which is what a review threshold would have been built
from, so it says so rather than silently reporting an overall rate.

Give it a results file with confidence on it instead and the same call finds the
operating point:

**AUTOMATE WITH REVIEW** — overall pass rate 85.0% [73.9%, 91.9%] misses the 90%
target; a candidate threshold at confidence 0.86 reaches 100.0% precision on 85% of cases on this selection set, but needs a fresh holdout before deployment

Candidate operating point (fresh holdout required): abstain below 0.86 → 85% cases selected at 100.0% precision 100.0% [93.0%, 100.0%], 9 cases to review
Calibration error 0.10
Note: The 0.86 threshold was chosen by searching this same case set, so its
precision is optimistically biased. Re-measure it on fresh cases before relying on it.

That last note is the plugin's own caveat, not a user's: a threshold found by
searching the case set it is scored on is biased upward. can_automate remains
false for this candidate; holdout signoff is a separate step.

The results file

One JSON object per line. passed alone is enough for a one-run verdict.
For gonogo_compare, every row in both runs needs the same stable, explicit
id (and matching group membership if grouped); generated row positions are
not paired evidence. The file is plain enough to build in a spreadsheet.

{"id": "inv-001", "passed": true,  "confidence": 0.97, "expected": "Acme Supply", "output": "Acme Supply"}
{"id": "inv-006", "passed": false, "confidence": 0.42, "expected": "Acme Supply", "output": "Cinder Works", "detail": "normalized the trading name to its parent entity"}
{"id": "inv-007", "passed": false, "error": "TimeoutError: 30s"}
{"id": "inv-008", "passed": false, "abstained": true}
{"id": "msg-014", "passed": true,  "confidence": 0.91, "expected_action": "no_reply", "action": "no_reply"}
{"id": "msg-015", "passed": false, "confidence": 0.88, "action": "wait"}
  • confidence (0–1) is optional for an entire file, but mixed missing and present
    values are rejected; it is what unlocks a candidate review threshold.
  • error is a crashed case — a failure, not an aborted run. abstained lands at
    zero confidence, so a threshold defers it first.
  • expected_action says the right move for a case is not to answer (wait,
    ask, no_reply, any name); absent means answer. action is what the agent
    did. A hold-back case passes exactly when they match, and any action other
    than abstain keeps its confidence: a confident wrong wait is a silent miss
    and counts against precision. Reports then carry the actions table (acted
    when it should have held back / held back when it should have acted). Name
    your say-nothing action no_reply or wait, not abstain.
  • group on every case folds correlated checks into one trial per group.
  • examples/invoice_routing.jsonl is a 60-case synthetic set to try it on.

gonogo_report with save_report writes the report JSON, and gonogo_compare
pairs two of those by case id (or by group id with matching case membership) —
so a run measured today can be checked against one measured last week.

What it can touch

Three tools, and nothing else: no hooks, no middleware, no environment
variables, no credentials, no network. Count-only calls need no file; results
calls read the path you give them and optionally write report JSON or HTML when
requested. The whole blast radius is the one dependency and those opt-in files.

Development

uv venv .venv && uv pip install --python .venv/Scripts/python.exe gonogo-eval pytest pyyaml
.venv/Scripts/python.exe -m pytest tests -q

hermes plugins validate .     # the catalog admission gate, including the security scan
hermes plugins doctor . --ci  # discovery, manifest, import, registration

The tests assert the verdicts themselves — that 47/50 at a 95% target is
ASSIST ONLY, that the same 47/50 at 90% is INSUFFICIENT EVIDENCE needing
about 204 cases, and that a 3-point gap on 60 cases is not an improvement.

MIT. The statistics are gonogo's; this plugin
is the surface that lets an agent ask for them.

README from the reviewed commit faf3e6a; it updates when the author re-pins.

More by keppy

3 plugins by keppy in the catalog.

keppyevalrouteRoute a task to the right (model, reasoning effort) arm before you start. Classifies the task into a lane, prints a provenance-carrying route card, and installs per-model reasoning-effort defaults. Strong rules run locally; weak-signal classification calls the host LLM and consumes tokens. The separate harness and shim can make paid provider calls when explicitly run; no plugin-owned credentials. Disclosure — weak-signal task classification sends the task text to your active Hermes model via the host LLM facade (token cost). The model-callable tool, /route, /rate and observed /model and /reasoning commands append task text and command args to a local ledger at <HERMES_HOME>/evalroute/labels.jsonl. "hermes evalroute install-routes" rewrites agent.reasoning_overrides in config.yaml when you run it. The bundled harness and Hermes shim are never run by the plugin. When run by hand they make paid provider calls, execute model-generated code on the host via shell checkers, and run hermes -z with approvals bypassed. Disclosure — "hermes evalroute sync" (run by hand) downloads the route table from the Hugging Face dataset keppy/evalroute-flywheel into HERMES_HOME and route cards then show that dataset's text; "hermes evalroute dispatch" (run by hand) spawns a single-query hermes chat on the routed model (Nous provider by default) — or, with --runner claude-code, a `claude -p` (Claude Code) process under the user's own Claude account, whose per-run cost is recorded in the local sidecar — with the brief as its query, inheriting the current environment except HERMES_YOLO_MODE (the worker never inherits --yolo; its approvals follow approvals.single_query_mode, default deny), and writes its report and a .dispatch.json sidecar beside the brief. Disclosure — "hermes evalroute contribute" (run by hand only) refuses to run unless evalroute.contribute: true is set in config.yaml, and then uploads redacted outcome rows only — lane, routed arm, confirmed arm, verdict, method, facet counts, ISO week and an HMAC-SHA256 task hash; never task text, notes, paths or the salt — to the user's own path (contributed/<hf-username>/) in the Hugging Face dataset keppy/evalroute-flywheel using the user's existing huggingface_hub login; "--dry-run" prints the exact rows and sends nothing; the per-install salt stays local in <HERMES_HOME>/evalroute/contribute.salt. Since evalroute 0.9.1 (schema 2) the same upload also carries lane-correction rows as lane pairs (from_lane, to_lane, method, week) with no task text, plus the max_turns value and harness name the arm ran under and, since evalroute 0.10.0, whether the arm was an explore draw and which default arm it replaced (arm ids only). Disclosure — since evalroute 0.10.0, setting evalroute.explore.fraction in config.yaml (or EVALROUTE_EXPLORE) makes that fraction of routes recommend the lane's runner-up arm from the bundled explore table instead of the default arm (and "hermes evalroute dispatch" then runs that runner-up model), marked on the route card; off by default. Disclosure — "hermes evalroute install-encoder <dir|owner/name>" (run by hand only) downloads a small text-classification model (the default id keppy/evalroute-lane-encoder is a 146 MB ModernBERT fine-tune on the Hugging Face Hub) into <HERMES_HOME>/evalroute/encoder/ and from then on "hermes evalroute route" runs it locally, offline, to classify the task when the keyword rules are weak, instead of the LLM call; it needs torch and transformers in the plugin environment (pip extra evalroute[encoder]), which the plugin does not install; "install-encoder --remove" deletes it. "hermes evalroute export-cases" (run by hand only) writes your --lane-pinned route rows, task text included, to a local file you name, for training your own encoder; stdout carries counts only and nothing is uploaded. As of 0.6.2 the plugin is a thin adapter: all behaviour lives in the PyPI package evalroute (>=0.10.0,<0.11), declared as a Python dependency that Hermes installs on your confirmation.Communitytools1keppythomasFine-tune a small calibrated encoder classifier on Modal and rate it with gonogo. Every GPU launch goes through the human approval gate with the exact config shown; data checks and evaluation run locally for free. Disclosure — thomas_encoder_train spends the user's Modal GPU credits and uploads the chosen training JSONL rows to Modal after the human approval gate; the base model is downloaded from Hugging Face on the Modal side. Training and inference run in a separate Python interpreter the user configures via THOMAS_PYTHON (with the author's thomas package, torch and modal installed); the Hermes venv only gets gonogo-eval. Runs and artifacts are written under HERMES_HOME/thomas/runs; the eval tool writes report files to the path the model supplies.Communitytools