fix(sn39): bind every external score source to one audience and fence - #136
Conversation
Audience was enforced for cathedral_confidential_tdx only. violet_audio and cathedral_sat_fast were accepted with a foreign network/netuid or with no audience at all, and their reports shared a single fence row per source: the epoch monotonicity gate selected on source alone, so a testnet report at a high epoch locked the mainnet audience out of its own source with epoch_too_old. The Postgres advisory lock was already keyed on (source, network, netuid) while the gate it protects ignored them, so lock and gate serialised different things. Audience is now a property of the publisher, not the source label: configured_score_audience() resolves one (network, netuid) for every source, intake requires an exact match, and the fence, the idempotent-retry lookup and the snapshot/status reads are all keyed on it. The lock and the gate now cover the same rows. The SAT fast poster stamps the audience it scored for, from the same env the publisher reads, and refuses to post when that env is unset.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66b6528b5c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if not isinstance(network, str) or type(netuid) is not int: | ||
| raise ExternalScoreError("invalid_score_audience") | ||
| if (network, netuid) != expected: | ||
| raise ExternalScoreError("score_audience_mismatch") |
There was a problem hiding this comment.
Update the public contract before requiring the audience
Clients following the checked-in API contract will now have every report rejected: docs/VIOLET_EXTERNAL_SCORES.md:215-247 omits network, labels netuid optional/unvalidated, and even demonstrates netuid 49, while this code requires both fields to exactly match the publisher (the shipped SN39 configuration is finney/39). Only the in-repo SAT poster was migrated, so existing Violet integrations using the documented payload will receive invalid_score_audience or score_audience_mismatch and external scoring will silently degrade to base-only; update the documented schema and external producers as part of this rollout, or provide a compatibility migration.
Useful? React with 👍 / 👎.
Audience was enforced for cathedral_confidential_tdx only. violet_audio and
cathedral_sat_fast were accepted with a foreign network/netuid or with no
audience at all, and their reports shared a single fence row per source: the
epoch monotonicity gate selected on source alone, so a testnet report at a high
epoch locked the mainnet audience out of its own source with epoch_too_old.
The Postgres advisory lock was already keyed on (source, network, netuid) while
the gate it protects ignored them, so lock and gate serialised different things.
Audience is now a property of the publisher, not the source label:
configured_score_audience() resolves one (network, netuid) for every source,
intake requires an exact match, and the fence, the idempotent-retry lookup and
the snapshot/status reads are all keyed on it. The lock and the gate now cover
the same rows.
The SAT fast poster stamps the audience it scored for, from the same env the
publisher reads, and refuses to post when that env is unset.
Opened from the unmerged
fix/audience-enforcementbranch (commit 66b6528, 2026-08-13). The work was written and pushed but never raised as a PR, so it was not inmain.This is also the exact gap raised on #118:
AUDIENCE_REQUIRED_SOURCESwas left at a single source while the other fail-closed sets were extended. The fix already existed on this branch.