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:
- pulls your decisions over the system's own read API (a credential file, never a key in a flag or a log);
- matches each delivered item's text one-to-one to the gateway's captured draft for the
bound call type, by content (the pure
diffStatsline diff) with explicit thresholds and a first-class abstention band — it never guesses which draft a decision belongs to; - records a
correctionoutcome withsource: imported:accepted_unchangedwhen 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.
Connect a check (oracles)
Register the check that knows the right answer — a platform rule, an HTTPS endpoint we call, or a worker that claims jobs — and let it earn the right to certify fixes. API reference level.
The training-ready export
What the Flywheel export is, what `not_trainable` means, and the consent that gates it. Training on your content is a separate, governed decision — it runs only for your own workspace, never a shared or cross-workspace model.