ultrarouter docs
Guides

Plug in your decision record

Turn the approve/reject record you already keep into correction outcomes with ZERO integration — a decision connector pulls your owner decisions, matches each delivered item to the draft the gateway captured, and records the result. Plus the experimental echo measurement and what it does and does not see.

If a human already decides whether each thing your agent produced was good — approved it, edited it, rejected it, asked for a fix — then you have an outcome record today, before you integrate anything. A decision connector reads that record from the system you already run, matches each decided item back to the draft the gateway captured, and records a correction outcome for it. You write no code and call no new endpoint.

The Flywheel is a preview under construction (local stack only, not on the hosted service). The outcome ledger, the artifact API and this connector are built; the learning and lift surfaces they feed are reported as explicitly incomplete by GET /v1/flywheel/status. A connector imports ground truth — it proposes no change and ships no treatment.

The idea

A connector is a small adapter behind one generic port:

pull(since) → [{ external_id, final_text, decision, decided_at, ... }]

decision is one of approved · rejected · invalidated · fix_requested — the human's decision, kept per delivered item. The connector:

  1. pulls your decisions over the system's own read API (a credential file, never a key in a flag or a log);
  2. matches each delivered item's text one-to-one to the gateway's captured draft for the bound call type, by content (the pure diffStats line diff) with explicit thresholds and a first-class abstention band — it never guesses which draft a decision belongs to;
  3. records a correction outcome with source: imported: accepted_unchanged when the approved item is identical to the draft, edited (with diff stats) when it differs, rejected, and a fix request as its own typed value.

Matching is idempotent (keyed on the connector + the item's external id + the kind) and resumable (pass --since). A re-pull is a no-op, never a double count. Bodies are read in memory only; a recorded outcome carries diff stats, lengths and bucketed similarity — never your content.

Pipeline repair vs the human's decision

Some systems run their own automated repair on a draft before a human ever sees it. When an approved item differs from the draft, the connector records the change honestly: it marks the difference pipeline_repair when a repair ran, or unattributed otherwise — it never reads a machine's rewrite as if a person had edited the text. The human's decision and the pipeline's repair stay separate on the row.

socialvp: the first connector

socialvp keeps one owner decision per studio delivery (approved = delivered, rejected = discarded, invalidated; a correction the owner asked for is fix_requested). The connector reads them over socialvp's platform-admin GET …/studio-deliveries (with the delivered post manifest) and matches each short post to the gateway's captured ct_short_write draft.

pnpm flywheel:connect socialvp pull \
  --workspace <ultrarouter-workspace-id> \
  --api-origin https://<your-socialvp-host> \
  --credential-file ./socialvp-connector.json \
  [--since 2026-10-01T00:00:00Z] [--dry-run]

The credential file is JSON and never printed:

{ "mode": "admin_assertion", "secret": "<base64url>", "workspaceId": "<socialvp-uuid>", "credentialVersion": 3 }

Least-privileged credential. socialvp's read API is reached only with its platform-admin assertion (a short-lived signed token) or, on a dev stack, the development header triple — both are read/write admin credentials; there is no read-only "deliveries" scope today. Until socialvp adds one, hold the connector credential as you would any admin secret, scope it to the one workspace, and prefer --dry-run first. (Issue for socialvp: a read-only studio-deliveries scope the connector can present.)

--dry-run pulls and matches but writes nothing, and prints a body-free summary: how many decisions were pulled, how many matched (by kind and by change attribution), how many abstained (by reason) and how many had no draft.

The echo measurement (experimental)

There is a second, zero-integration signal: if a draft reappears in a later captured request — for example in a "recent posts" context the writer is given — that is evidence it was published. The echo rule (nt.echo_published, v1) detects an earlier draft reappearing in a later body and emits inferred correction candidates, abstaining in a borderline band.

It is experimental and never teaches. Its candidates are learning_use: audit only; they never feed training or calibration. Its purpose is measurement:

pnpm flywheel:connect echo-measure --workspace <id> --call-type ct_short_write [--since <iso>]

scores the echo's precision and recall against the connector's imported truth, and reports the sample size n alongside every rate.

What the echo does and does not see. In socialvp the recent-post list is the last few deliveries with no owner-decision filter, and a draft is also fed to its own review/repair stage — so an echo can reflect the pipeline's own processing, not the human's keep decision. On recorded socialvp runs the echo's precision was well below one (many reappearances were not kept), with high recall. Read the echo as a coverage probe for the connector's ground truth, never as the decision itself.