Agent Usage Feedback Loop
This document defines the usage feedback loop for comparing agent runs across repositories, runtimes, profiles, prompts, skills, and future setups. The measurement schema is runtime-neutral: every runtime should import or emit normalized session, event, and outcome records before reporting. Codex and Claude Code can both flow through the shared telemetry envelope; Kiro uses the same canonical telemetry script when hooks are installed.
The loop has three parts:
- Import or read runtime telemetry into normalized usage records.
- Sync quality outcomes from task artifacts, evals, release gates, or humans.
- Report usage and outcome metrics by repo, runtime, profile, prompt, skill, or task type.
Quality outcomes are not inferred from raw telemetry alone. The CLI can automatically derive coarse outcomes from task artifacts such as .kontourai/flow-agents/<slug>/*.md: delivered and complete become success, failed becomes failure, and optionally open artifacts can be recorded as not_verified. It does not invent quality_score or human_minutes_saved; those remain human/eval/release-gate facts.
Retrospective run observations
usage-feedback compile-observation compiles one exact correlated run from a
manifest of producer-owned records. The manifest names logical source IDs and
the exact runtime event, Builder state, trust bundle, Flow state, Flow evidence
manifest, economics, and terminal-outcome files. Compilation never joins by
path, timestamp, task slug, cwd, process ancestry, or content similarity.
The output is a versioned
kontour.flow-agents.retrospective-observation record under
.kontourai/flow-agents/feedback/observations/<observation-hash>.json by default.
One atomically replaced, locked file per run avoids cross-run lost updates. It contains
stable identity, process and verification status, measured usage, explicit
missing dimensions, and content hashes plus logical IDs for local drill-down.
It contains no source paths, prompts, tool payloads, source content, or secrets.
Repeat compilation of unchanged sources is byte-identical and upserts the same
observation ID.
Process completion and Flow verification are not independent task quality.
Until an external evaluation is joined through the eval-owned boundary, every
compiled observation reports quality as NOT_VERIFIED with reason
not_independently_evaluated. The compiler emits no causal claim about a Kit,
model, prompt, or workflow.
npm run usage-feedback -- compile-observation \
--manifest .kontourai/flow-agents/run-observation-manifest.json \
--record-root .
To inventory real local usage without crawling arbitrary home directories, pass each operator-configured record root explicitly:
npm run usage-feedback -- compile-corpus \
--record-root /path/to/runtime-or-repository-root \
--record-root /path/to/another-configured-root
compile-corpus inspects only known producer locations under those roots:
.kontourai/telemetry, .kontourai/flow-agents, and
.kontourai/flow/runs. It never joins records by path similarity, timestamps,
working directory, or process ancestry. Records without an exact producer
run_correlation remain counted as uncorrelated.
Each configured source is parsed once per invocation. Explicitly correlated records and exact Flow/trust secondary records are retained in one bounded snapshot and reused across that invocation’s per-run projections; report facts are not restored from an unsigned cache. JSONL producers may append while the inventory runs: the compiler double-hashes the opening-length prefix, records that accepted prefix in the watermark, and verifies it again before publication. Appends after the accepted prefix are deferred to the next generation. Prefix mutation, truncation, replacement, invalid UTF-8, and resource-limit exhaustion fail closed.
The command writes:
feedback/corpus/current.json: the atomically replaced pointer to the current complete generation;feedback/corpus/generations/<watermark>/watermark.json: content hashes and stable root-local file identities for the observed producer snapshot;feedback/corpus/generations/<watermark>/report.json: coverage, correlation, compilation, completeness, ambiguity, and quarantine counts;feedback/corpus/generations/<watermark>/manifests/*.json: exact root-relative source declarations for compiled runs;feedback/corpus/generations/<watermark>/observations/*.json: privacy-safe per-run observations.
Reports always keep causal and quality effects NOT_VERIFIED. Observational
coverage can generate hypotheses for Evals, but it cannot establish that a Kit
caused an outcome.
The manifest uses the
retrospective-observation-manifest/1.0 schema. Every individual source is
optional so an incomplete historical run can still compile honestly, but at
least one producer record must carry the exact manifest correlation ID. Source
files are relative to the caller-selected --record-root; traversal, absolute
paths, empty or dot path segments, and symbolic links are rejected. The record
root and output directory are operator trust boundaries, not data supplied by
a producer. Their directory chains must be non-group/world-writable, except for
sticky shared ancestors such as the system temporary directory, and each final
boundary directory must be owned by the current operator. Source files must be
owned by the current operator or root and must not be group/world-writable.
Concurrent replacement or mutation by another process running as the same OS
principal is outside this local compiler’s filesystem trust boundary; the
accepted-prefix checks detect such changes during an invocation:
{
"schema_version": "1.0",
"correlation_id": "run-correlation-id",
"sources": {
"runtime_events": {
"source_id": "runtime-events",
"file": ".kontourai/telemetry/full.jsonl"
},
"terminal_outcome": {
"source_id": "terminal-outcome",
"file": ".kontourai/flow-agents/task/workflow-outcome.json"
}
}
}
Runtime activity, Flow history, trust, economics, and terminal status are
projected only when their complete correlation envelopes equal the canonical
run envelope. Sharing a correlation_id alone is insufficient. A trust
dimension additionally requires a Surface-valid bundle and one exact
Flow-manifest attachment whose embedded bundle and SHA-256 match the supplied
bundle.
Usage describes a present run delta only when terminal usage and economics
agree on timestamp and token totals. CONFIRMED additionally requires an
authenticated runtime binding and exactly matching model, duration, delegation,
and cost attributes. When token totals agree but another attribute is
unavailable or disagrees, the compiler retains the token counts under
NOT_VERIFIED and emits null for each unmatched attribute rather than
fabricating or discarding useful consumption data. Fixture authority is always
NOT_VERIFIED.
Malformed input and partial reports
Read-only reports, telemetry-source imports, and retrospective compilation
quarantine malformed JSONL records and continue over valid records. JSON reports expose a measurement object with
total, valid, and malformed counts plus content-free diagnostics; Markdown
reports show the same counts under Measurement State. The CLI also emits one
stderr warning per affected logical source. Diagnostics include only a logical
source name, line number, SHA-256 content hash, and parse error class. They never
include the malformed record or an absolute path.
Retrospective compiler diagnostics also classify schema-valid JSON that fails a
producer contract as ProducerValidationError; counts remain separate from
syntax failures and source bytes never enter the observation.
Corpus compilation keeps malformed and producer-invalid counts on every affected source reference and in the corpus report, but does not copy the same source-level diagnostic into every observation that shares a multi-run source. Run-specific projection failures still appear on that run’s observation.
This tolerance applies only to source analysis. A destination that an import, sync, or upsert operation would rewrite remains strict: one malformed existing record aborts before any write. The command does not silently discard corrupt state.
Storage Defaults
Repo-local telemetry defaults to .telemetry/:
.telemetry/full.jsonl: raw runtime telemetry events..telemetry/analytics.jsonl: summarized analytics emitted by telemetry hooks..telemetry/sessions/: per-session runtime artifacts, when the runtime writes them..telemetry/outcomes.jsonl: human/eval outcome records for completed work..telemetry/reports/: generated Markdown or JSON usage reports.
Locations are configurable. CLI commands should accept --telemetry-dir, and shell workflows may set TELEMETRY_DATA_DIR. User-level or global Codex telemetry roots, such as ~/.codex/telemetry or ~/.flow-agents/telemetry, should be treated as configurable installation choices, not hard-coded truth. When comparing multiple repositories or machines, pass each telemetry root explicitly with repeatable --telemetry-dir flags.
Runtime hooks can also mirror redacted telemetry to Console without changing the
event shape. Set CONSOLE_TELEMETRY_URL=http://127.0.0.1:3737 for a local
Console, or CONSOLE_URL=https://console.kontourai.io for a deployed Console.
The transport posts to /api/telemetry/records by default. Hosted Console URLs
must use https://; http:// is accepted only for localhost or 127.0.0.1
local development. Use CONSOLE_TELEMETRY_ENDPOINT_URL only when the API path
is nonstandard, CONSOLE_TELEMETRY_TOKEN or CONSOLE_AUTH_TOKEN for bearer
auth, and CONSOLE_TENANT_ID for hosted tenant routing. If no Console URL is
set, telemetry remains local-only.
Flow Agents owns the Console telemetry descriptor at console.telemetry.json.
Generated bundles include that root descriptor beside AGENTS.md, scripts, and
kit assets. The descriptor maps Flow Agents runtime telemetry and workflow
sidecars into generic Console facets for skills, tools, flows, repositories,
projects, runtimes, agents, models, statuses, and outcomes. Console consumes
those mappings as product-owned display metadata; Flow-owned gate and transition
semantics stay in Flow contracts and sidecars. Repository and project metadata
are stable cross-user workspace identifiers when present. Local working-directory
paths stay out of the descriptor so Console display metadata does not expose
usernames or machine-local paths; records without a repository identifier should
fall back to their logical product root such as product:flow-agents:.kontourai/flow-agents (the runtime artifact root; #778 corrected the descriptor’s own record-source roots to match after they drifted from the .flow-agents -> .kontourai/flow-agents runtime-root migration).
Packaged setup modes are:
local-files: default local JSONL telemetry only; no Console URL or token.local-kontour-console: mirror to a separately running local Console, usinghttp://127.0.0.1:3737unlessFLOW_AGENTS_LOCAL_KONTOUR_CONSOLE_URLis set.kontour-hosted-console: mirror to Kontour’s hosted Console default URL. Pass--console-token-fileand--console-tenantfor headless hosted setup.user-hosted-console: mirror to a self-hosted Console; requires--console-urlor--console-endpoint.
Use flow-agents init --yes or --headless with the same flags in CI. The
legacy sink names kontour-cloud and hosted-kontour-console are still
accepted for existing scripts.
Owner machine mirror
For a personal machine or repo where an owner wants Claude Code (or other
runtime) hook sessions mirrored to a hosted Console, without editing the
tracked scripts/telemetry/telemetry.conf default template (that file ships
verbatim into every packaged bundle, so writing a personal token/tenant into
it would leak to downstream consumers) and without exporting env vars per
session, scripts/telemetry/lib/config.sh auto-discovers a gitignored,
operator-created conf at either of two conventional paths, no extra wiring
required:
<workspace>/.kontourai/telemetry-console.conf— repo-scoped, checked first.~/.flow-agents/telemetry-console.conf— machine-scoped, used when no workspace-scoped conf is present.
Populate either path with the existing preset installer:
`scripts/telemetry/install-console-config.sh