Skip to content

fix(sn39): bind every external score source to one audience and fence - #136

Merged
wallscaler merged 1 commit into
mainfrom
fix/audience-enforcement
Aug 15, 2026
Merged

wallscaler merged 1 commit into
mainfrom
fix/audience-enforcement

Conversation

@wallscaler

Copy link
Copy Markdown
Contributor

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-enforcement branch (commit 66b6528, 2026-08-13). The work was written and pushed but never raised as a PR, so it was not in main.

This is also the exact gap raised on #118: AUDIENCE_REQUIRED_SOURCES was left at a single source while the other fail-closed sets were extended. The fix already existed on this branch.

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.
@cursor

cursor Bot commented Aug 15, 2026

Copy link
Copy Markdown

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +102 to +105
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")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

@wallscaler
wallscaler merged commit 47c3525 into main Aug 15, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant