Skip to content

fix(prism-emit): carry positive scores across epochs - #75

Merged
echobt merged 1 commit into
mainfrom
hotfix/prism-weight-carry
Aug 7, 2026
Merged

fix(prism-emit): carry positive scores across epochs#75
echobt merged 1 commit into
mainfrom
hotfix/prism-weight-carry

Conversation

@echobt

@echobtechobt commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Prism epoch-close emission kept exactly-once outbox assignment, but positive lattice scores now re-enter every later epoch's competition set until a better score supersedes via max (lattice-proportional, not WTA).
  • Fixes the prod failure mode where a reject-only follow-up epoch (Score(0)) burned the prism share after a prior winner had already been stamped with emitted_epoch.
  • Design already carries via the rolling window; gateway last-known-good remains forbidden by BUNDLE_SPEC.

Test plan

  • cargo test -p prism-emit -p prism-store
  • New regression: score at epoch N → reject-only N+1 still emits winner Score
  • cargo fmt --check, clippy on touched crates, external-docs-check, loc-cap
  • CI green
  • Surgical prod promote of prism-challenge only (no gateway/metagraph/real-seal churn)

Summary by CodeRabbit

  • New Features

    • Positive scores now remain active in later competition epochs until replaced by a better valid score.
    • Fresh finalized results and active positive scores are combined when producing emission batches.
    • Zero-score and no-score results are excluded from carried-over competition entries.
    • Recovery after interruptions includes both pending results and active winners.
  • Documentation

    • Updated orchestration, emission, completeness, and miner guidance to describe score carryover and replayable assignment behavior.

Exactly-once outbox assignment left winners silent after the first emit, so a
reject-only follow-up epoch burned the prism share. Re-include active Score>0
rows in every epoch's competition set until a better score supersedes via max.
@coderabbitai

coderabbitaiBot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The emitter now combines fresh outbox rows with active positive scores during each epoch. Store implementations provide eligible scores, tests cover carry-forward and recovery behavior, and documentation describes the updated emission semantics.

Changes

Cross-epoch score carry

Layer / File(s)Summary
Active score retrieval
crates/prism-store/src/store.rs, crates/prism-store/src/emit.rs, crates/prism-store/src/dbprism.rs
The store API returns positive score rows for carry-forward. Database and memory stores exclude zero and absent scores.
Competition input assembly
crates/prism-emit/src/lib.rs
emit_rows merges fresh outbox rows with active positive scores before leaf construction. EmitSummary.batch documents fresh outbox assignment.
Epoch semantics and documentation
crates/prism-emit/tests/epoch_semantics.rs, crates/prism-challenge/src/orchestrator.rs, docs/*.md, docs/external-miner/prism.md
Tests and documentation cover persistent positive scores, reject-only epochs, exactly-once assignment, and crash recovery.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
participant emit_rows
participant PrismStore
participant LeafBuilder
emit_rows->>PrismStore: retrieve fresh outbox rows
emit_rows->>PrismStore: retrieve active positive score rows
emit_rows->>emit_rows: merge competition rows
emit_rows->>LeafBuilder: build signed leaves
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly and concisely describes the main change: carrying positive scores across epochs in prism-emit.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/prism-weight-carry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/prism-emit/src/lib.rs`:
- Around line 167-171: Persist the complete competition inputs or signed leaves
for each assigned epoch in emit_assigned instead of relying on a fresh
active_score_rows read during recovery. Update replay to use that stored
snapshot so first-write-wins leaves remain identical after later score
finalization, and add a recovery test covering a higher score finalized between
the initial submission and replay.
In `@docs/PRISM.md`:
- Around line 113-143: Update the competition-credit rules in the next section
of docs/PRISM.md to use the complete competition set passed by emit_rows to
competition_scores, including carried active positive scores, rather than only
the epoch outbox batch. Explicitly distinguish batch assignment from the unioned
competition input while preserving the normative documentation wording.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 93a5e24a-1c1c-4916-948c-b05b72fcc73f

📥 Commits

Reviewing files that changed from the base of the PR and between 9ad01ec and 1ff5bfd.

📒 Files selected for processing (9)
  • crates/prism-challenge/src/orchestrator.rs
  • crates/prism-emit/src/lib.rs
  • crates/prism-emit/tests/epoch_semantics.rs
  • crates/prism-store/src/dbprism.rs
  • crates/prism-store/src/emit.rs
  • crates/prism-store/src/store.rs
  • docs/COMPLETENESS.md
  • docs/PRISM.md
  • docs/external-miner/prism.md

Comment on lines +167 to +171
let active = self.store.active_score_rows(self.netuid).await?;
let competition = merge_competition_rows(&batch, &active);
let owners: BTreeMap<String, String> =
self.store.arch_owners().await?.into_iter().collect();
let signed = build_epoch_leaves(&self.sk, epoch, expected, &batch, &owners)?;
let signed = build_epoch_leaves(&self.sk, epoch, expected, &competition, &owners)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Persist the competition snapshot for recovery.

emit_assigned also reaches this dynamic active_score_rows read. If a positive score finalizes after an epoch set was submitted but before the cursor advances, recovery rebuilds that old epoch with different leaf values.

This breaks the documented identical replay requirement for first-write-wins gateway leaves. Persist the complete competition input or signed leaf set per assigned epoch, and replay that snapshot. Add a recovery test that finalizes a higher score between the initial submit and replay.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/prism-emit/src/lib.rs` around lines 167 - 171, Persist the complete
competition inputs or signed leaves for each assigned epoch in emit_assigned
instead of relying on a fresh active_score_rows read during recovery. Update
replay to use that stored snapshot so first-write-wins leaves remain identical
after later score finalization, and add a recovery test covering a higher score
finalized between the initial submission and replay.

Comment threaddocs/PRISM.md
Comment on lines +113 to +143
**Leaf emission (epoch-close, exactly-once outbox + score carry).** A
submission row's acceptance epoch (`prism_submission.epoch`) is intake
metadata only. A dedicated emitter loop (`prism-emit`, one tick per chain
epoch) emits **one D24-complete leaf set per chain epoch**: the first tick
that observes epoch `E` assigns every submission finalized since the
previously emitted epoch — the outbox batch,
`kind IS NOT NULL AND emitted_epoch IS NULL` — to `E`, competition-aggregates
that batch **unioned with every still-active positive lattice score**
(`kind = 'score' AND score > 0`), signs the full expected set
(`NoScore(NotAttempted)` for everyone else), submits it, and advances the
per-netuid emit cursor (`prism_emit_cursor`, migration 0012). This fixes the
two acceptance-epoch bugs: independent scorers finalized in the same epoch
used to lock each other out (gateway leaves are append-only first-write-wins
per `(challenge, epoch, hotkey)`), and a submission accepted in epoch `X` but
finalized in `X+k` (prod trains up to 6h ≫ 72-min epochs) never scored at
all.

Exactly-once **outbox assignment** per scoring run: batch assignment is sticky
before submit, the cursor advances only after the full set landed, and a crash
mid-submit replays the identical assigned set on the next tick
(first-write-wins with identical values converges). After assignment, a
positive `Score(v>0)` keeps participating in every later epoch's competition
set until a better/valid score supersedes it via lattice `max` (not WTA) — so
an empty or reject-only fresh batch does not burn the prism share. `Score(0)`
rejects and `NoScore` absences do not carry. A manually retried + re-scored
row re-enters the outbox (`reset_for_retry` clears the watermark); its old
leaf stays immutable history in its original epoch. Epochs during a master
outage carry no *new* outbox rows; the first epoch after recovery still
includes active positive scores plus any backlog (seals always pin fresh
epochs — stale bundles can never Match on-chain). Run **exactly one**
prism-challenge emitter instance per netuid (single master topology).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the competition-credit wording with score carry.

The next section still defines challenger and architecture-owner credit from the epoch batch. emit_rows now passes the union of the batch and active scores to competition_scores, so carried scores also affect these credits.

Replace “batch” in those credit rules with the complete competition set, or explicitly distinguish the two inputs. As per coding guidelines, “Treat normative documentation—including architecture files, frozen specifications, threat and operator-security documents, completeness status, runbooks, and external-miner/—as the source of truth for contracts, operations, and status.”

🧰 Tools
🪛 LanguageTool

[grammar] ~130-~130: Ensure spelling is correct
Context: ...in epochs) never scored at all. Exactly-once outbox assignment per scoring run: ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/PRISM.md` around lines 113 - 143, Update the competition-credit rules in
the next section of docs/PRISM.md to use the complete competition set passed by
emit_rows to competition_scores, including carried active positive scores,
rather than only the epoch outbox batch. Explicitly distinguish batch assignment
from the unioned competition input while preserving the normative documentation
wording.

Source: Coding guidelines

@echobt
echobt merged commit 546463f into mainAug 7, 2026
3 checks passed
@coderabbitaicoderabbitaiBot mentioned this pull request Aug 16, 2026
Sign up for freeto 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

@echobt