Skip to content

feat(observability): emit review quality to PostHog as $ai_metric - #10228

Merged
JSONbored merged 1 commit into
mainfrom
feat/ai-quality-signals
Jul 31, 2026
Merged

feat(observability): emit review quality to PostHog as $ai_metric#10228
JSONbored merged 1 commit into
mainfrom
feat/ai-quality-signals

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Reviewer stances and inter-run agreement already reach SQL, Grafana and the maintainer recap, but never PostHog. So cost, model and latency were readable there while whether the review was any good was not — and the two halves could not be joined.

This emits them as $ai_metric against the review's own trace, so quality lands beside the spend and the model that produced it. With the trace grouping from #10187, a metric on that trace id sits directly alongside the generations it is measuring.

The contract comes from the SDK, not from a guess

@posthog/core's captureTraceMetric:

captureTraceMetric(traceId, metricName, metricValue) {
    this.capture('$ai_metric', {
        $ai_metric_name: metricName,
        $ai_metric_value: String(metricValue),
        $ai_trace_id: String(traceId)
    });
}

The value is stringified by the SDK, so this stringifies it too — a hand-built metric and an SDK-built one are indistinguishable downstream. A test pins that, including that a numeric 0 and a false become "0" and "false" rather than being dropped: a "did not flag" vote is real signal, not an absent one.

Two deliberate refusals

  • No ambient trace, no event. An orphan quality score joins to nothing and would only inflate counts.
  • An uncorroborated review reports no agreement at all. UNCORROBORATED_AGREEMENT (0.5) is a deliberate placeholder for "nothing to agree with", not a measurement — publishing it would drop a fabricated 0.5 into an average alongside real scores. That decision lives in a pure helper beside the scorer (judgmentAgreementMetrics), so both sides of it are testable directly instead of requiring a full dual-reviewer run to reach.

Only the confidence-independent half of the agreement score is reported. agreement and sampleCount are properties of the stances themselves; confidence folds in a per-finding verbalized confidence, so it belongs to a finding rather than to the review.

Closes #10226

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked a currently open issue this PR resolves (e.g. Closes #123) — a linked open issue is required for every contributor PR.

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally; codecov/patch requires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

Also run and green: npm run dead-exports:checkPOSTHOG_AI_METRIC_EVENT is referenced from the tests, following POSTHOG_MONITOR_HEARTBEAT_EVENT's existing pattern.

If any required check was skipped, explain why:

  • Coverage measured scoped to the three changed source files: 100% of the changed lines AND branches, verified line-by-line against lcov. The existing reviewer-vote and judgment-agreement suites are green alongside the new cases.
  • The unchecked commands cover untouched surfaces (no workflow, binding, schema, UI or MCP manifest change here) and are left to CI.

A coverage gap that changed the design

The first version guarded the agreement emit with if (!agreementSignal.uncorroborated) inline in the orchestrator. That branch's true side was unreachable from the existing test harness, which drives a single-reviewer review. Rather than bolt on a dual-reviewer fixture purely to reach it, the decision moved into judgmentAgreementMetrics — a pure function returning an empty list when there is nothing to report. The orchestrator now just iterates whatever it is given, and both sides are covered by direct unit tests. Better factoring, not a workaround.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests.
  • API/OpenAPI/MCP behavior is updated and tested where needed.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks.
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs.

The metric carries a name, a number and the already-processed operational context — no prompt, completion or finding text. Context routes through the shared operational allowlist, so an unlisted key is dropped (a test asserts it), and under the shared central key the repo is HMAC-anonymized and fail-closed exactly as every other event on this path. No reward, trust-score or private-scoring value is emitted — the metrics here are a reviewer's binary stance and an inter-run agreement ratio.

The emission is best-effort alongside the existing audit write, matching that call site's established discipline: the capture is a no-op when PostHog is off and never throws, so it cannot affect a review.

UI Evidence

Not applicable — no visible UI, frontend, docs, or extension change.

Notes

$ai_feedback and $ai_evaluation are deliberately not included, and the issue says why in full. In short: $ai_feedback is PostHog's user-feedback event, whose documented path is the Surveys integration — there is no human feedback at these call sites, and dressing a machine vote as one would misrepresent what the event means. $ai_evaluation has no property contract in the SDK or the docs beyond a one-line taxonomy description, so any shape chosen today would be invented. Worth revisiting once PostHog documents it, or once online evaluations are enabled — which content capture (#10218) unblocks.

Four further sources named in the issue (self-consistency, routing shadow, gate precision, eval score records) are left for follow-up; this lands the helper plus the two highest-value call sites.

Reviewer stances and inter-run agreement already reach SQL, Grafana and the
maintainer recap, but never PostHog -- so cost, model and latency were readable
there while whether the review was any GOOD was not, and the two halves could not
be joined.

Emit them as $ai_metric against the review's own trace, so quality lands beside
the spend and the model that produced it. The property contract is taken verbatim
from @posthog/core's captureTraceMetric -- name, value, trace id, with the value
stringified as the SDK does it -- so a hand-built metric and an SDK-built one are
indistinguishable downstream.

The trace id defaults to the ambient OTel trace every generation under the review
already carries. No ambient trace means no event: an orphan quality score joins to
nothing and would only inflate counts.

An uncorroborated review reports NO agreement rather than the
UNCORROBORATED_AGREEMENT floor. That constant is a deliberate placeholder, not a
measurement, and publishing it would drop a fabricated 0.5 into an average
alongside real scores. The decision lives in a pure helper beside the scorer, so
both sides of it are testable without driving a whole dual-reviewer run.

Closes #10226
@loopover-orb

loopover-orb Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-31 13:38:01 UTC

5 files · 1 AI reviewer · no blockers · CI green · clean

⏸️ Suggested Action - Manual Review

  • AI review did not produce public notes: The configured AI reviewer returned no usable public assessment for this PR head.

Review summary
AI review is unavailable for this PR head. LoopOver is holding this PR for manual review until the configured AI provider returns a usable public review summary.

Nits — 2 non-blocking
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • AI review did not produce public notes — Fix the configured AI provider, then re-run LoopOver review before relying on the result.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ⚠️ Gate result — Not blocking (Advisory; not blocking this PR.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #10226
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 9 registered-repo PR(s), 8 merged, 310 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 9 PR(s), 310 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Linked issue satisfaction

Partially addressed
The PR delivers the capture helper matching the SDK contract, the no-ambient-trace guard, and wires reviewer_vote and judgment agreement (correctly omitting the UNCORROBORATED_AGREEMENT floor) with good test coverage, satisfying the first, second and fourth checklist items. However, the issue explicitly lists self-consistency runs, routing shadow/provider track records, gate + rule precision, and

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, Ruby, Go, MDX, Shell, Solidity, JavaScript
  • Official Gittensor activity: 9 PR(s), 310 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Decision record
  • action: hold · clause: ai_review_inconclusive
  • config: 86ed9701e499552e64608b1c88058d591991184538769ac9a03aa6b13aeef2aa · pack: oss-anti-slop · ci: passed
  • record: ea77e3f60292a4ed28c39ac11f095e73cf92fa7dd9bcf471eb8b00d93a368eef (schema v6, head 18a5b32)

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@JSONbored JSONbored self-assigned this Jul 31, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.35%. Comparing base (800045f) to head (18a5b32).
⚠️ Report is 10 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #10228      +/-   ##
==========================================
- Coverage   92.22%   91.35%   -0.87%     
==========================================
  Files         937      937              
  Lines      114376   114389      +13     
  Branches    27654    27658       +4     
==========================================
- Hits       105482   104501     -981     
- Misses       7588     8777    +1189     
+ Partials     1306     1111     -195     
Flag Coverage Δ
backend 94.12% <100.00%> (-1.55%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/queue/ai-review-orchestration.ts 98.38% <100.00%> (+0.03%) ⬆️
src/review/judgment-agreement.ts 100.00% <100.00%> (ø)
src/selfhost/posthog.ts 99.47% <100.00%> (+0.01%) ⬆️

... and 3 files with indirect coverage changes

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 31, 2026
@JSONbored
JSONbored merged commit ff28627 into main Jul 31, 2026
8 checks passed
@JSONbored
JSONbored deleted the feat/ai-quality-signals branch July 31, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ai(observability): review quality never reaches PostHog — no $ai_metric is ever emitted

1 participant