Skip to content

orb(observability): the orphaned review_targets table silently disables the anomaly alerter, the reputation quality signal, and the AMS bridge — and the reputation half expires to empty around 2026-09-20 #9136

Description

@JSONbored

Problem

review_targets has no live writer anywhere — stated outright in the code itself
(src/db/repo-identity-rename.ts:585-586: "has NO live writer anywhere in this codebase";
src/review/public-stats.ts:275: "the convergence cutover orphaned (nothing writes it anymore)"). It
was orphaned by the 2026-06-22 convergence cutover.

Three subsystems still read it as their primary source and are therefore inert or expiring, with no
alarm on any of them.

1. The Discord anomaly alerter can never fire

runAnomalyAlerts bails at if (anomalies.length === 0) return;. detectAnomalies(health, calibration)
draws every line from computeAgentHealth / computeCalibration, whose queries are all FROM review_targets or JOIN review_targets t ON t.id = a.target_id (src/review/ops.ts:219-249, :294-299).
The remaining two inputs come from defaultOpsHealthDeps (alerts-wire.ts passes no deps):
validateAgentConfig: () => [] and isHoldOnly: async () => false. So configIssues is always [],
holdOnly always false, every count 0.

Even with LOOPOVER_REVIEW_OPS=1 and a valid DISCORD_WEBHOOK_URL, the alerter runs hourly, computes an
all-zero snapshot, and returns. The whole "reversals / DLQ spike / disputed closes / calibration drift /
manual-rate spike" channel is dead — and the same zeros are served to the operator by the /status and
/calibration ops endpoints as an apparently healthy snapshot.

There is a second, independent break: review_audit.target_id is "owner/repo#123"
(outcomes-wire.ts:275-277) while review_targets.id is a different id namespace, so the reversal/DLQ
joins could not match even if the table were repopulated.

2. The reputation quality signal is a slowly-expiring ghost

#9015 repointed getSubmitterCadence to pull_requests and left the comment "reads the LIVE ledger"
but getSubmitterReputation, getSubmitterReputationAcrossInstall, and listSubmitterCohortRows
(src/review/submitter-reputation.ts:302-312, :338-353, :388-399) still query FROM review_targets … terminal_at >= datetime('now', ?). With the ledger frozen at 2026-06-22 and a 90-day window, those
queries return a shrinking set that reaches permanently empty around 2026-09-20.

Consequences: promptInjection > 0 → "low" — the only hard-abuse rule in the reputation model — can
never fire on post-cutover data; "trusted" is unreachable, which makes the entire
src/review/ams-reputation-bridge.ts (an upgrade-only bridge whose sole output is "trusted") inert by
construction; the low_reputation skip in evaluateVisualVisionGate
(src/review/visual/visual-findings.ts:88) and evaluateScreenshotTableVisionGate
(screenshot-table-vision.ts:68) is inert and has no compensating hold — so restoring the signal
without fixing those reproduces #9015's "suspicion buys less scrutiny" shape.

3. The AMS-vs-human cohort dashboard silently empties

src/review/ams-miner-cohort.ts (#6488) degrades to present: false.

Dedup

#9015 is closed; its fix point 2 ("restore a live writer for review_targets or repoint the
cadence query") was satisfied by the narrower half only. Reporting the remainder as an incomplete fix.
The review_targets-frozen fact is noted in the prior audit's notes but no issue owns the consequences.

Requirements

  • Decide once: repoint every reader at the live ledgers (pull_requests + audit_events with the
    owner/repo#n target key, as public-stats.ts and ops-wire.ts already do), or restore a writer.
    Do not leave both halves half-done again — enumerate every reader in the PR description.
  • Fix the target_id namespace mismatch in the reversal/DLQ joins.
  • Add a startup/health assertion that each reputation and ops source table has rows newer than its own
    window, so the next orphaning is loud rather than silent. This is the generalisable fix — the same
    class produced orb(db): the review-effort metric is silently broken on Postgres — avg(text) errors and the failure is swallowed #9084 and the DLQ dead-alert findings.
  • Before restoring the reputation signal, give the two vision-gate skips a compensating hold.

Test Coverage Requirements

99%+ patch coverage, branch-counted; a test per repointed query asserting non-empty results against live
fixtures, and both arms of the staleness assertion.

Links & Resources

maintainer-only — observability and reputation integrity.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions