Skip to content

fix(seal): stop design/real-seal epoch desync - #116

Merged
echobt merged 2 commits into
mainfrom
fix/seal-epoch-sync
Aug 10, 2026
Merged

fix(seal): stop design/real-seal epoch desync#116
echobt merged 2 commits into
mainfrom
fix/seal-epoch-sync

Conversation

@echobt

@echobtechobt commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Root cause (prod): Design late-tempo emit re-read the chain schedule inside emit_leaves, so an epoch-boundary flip could relabel the leaf set as E+1 and permanently skip E. base-real-seal only tried the current epoch → perpetual D24 409, while /v1/weights/latest stayed pinned on an old chain-scale seal (burn seals cannot outrank it). Observed: seal stuck at 24412 for ~11 epochs while challenges were on 24423.
  • Design emitter: pin epoch/block from the tick snapshot (no re-read), sequential catch-up for skipped epochs, wider late window (96 blocks).
  • prod-real-seal.sh: walk back up to 16 sealable epochs; clearer logs.
  • Validator pressure/verify: WARN validator_seal_lag / validator_seal_metagraph_stale when sealed epoch or metagraph block lags the chain.

Ops evidence

  • Prod base-real-seal.log: 409 from 24413→24423 until walk-back script deployed.
  • Hot-patched script on base-prod sealed epoch 24423 (metagraph_block 8815687).
  • Still need image deploy of design-challenge (+ validator) for the emitter + pressure-verify log fix.

Test plan

  • cargo test -p design-challenge --lib -- emit_plan
  • cargo test -p validator --lib -- tick_pressure_verify
  • clippy on touched crates
  • After merge: deploy design-challenge + validator; confirm no multi-epoch seal lag and validator logs pressure events only when lagging

Summary by CodeRabbit

  • New Features

    • Added reliable catch-up emission for missed epochs, including pinned epoch-start blocks.
    • Added configurable backward traversal when sealing epochs, with retries for incomplete participant sets.
    • Added support for reading chain tempo from multiple storage formats.
  • Bug Fixes

    • Prevented epoch-boundary races from mislabeling emissions.
    • Improved seal response handling so only successful requests are accepted.
    • Added warnings for significantly outdated coordination data without blocking processing.

Design re-read the chain schedule inside emit_leaves at late tempo, so an
epoch-boundary flip could relabel the set as E+1 and permanently skip E.
Real-seal only tried the current epoch, leaving /weights/latest stuck on an
old chain-scale bundle. Catch up skipped epochs, widen the late window, walk
back sealable epochs in prod-real-seal, and add validator pressure-verify
logs when the sealed epoch lags the chain.
@coderabbitai

coderabbitaiBot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@echobt, you've reached your PR review limit, so we couldn't start this review.

Next review available in:25 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f324464-d24a-4c6f-9a36-31ef61a8f76c

📥 Commits

Reviewing files that changed from the base of the PR and between 2658f73 and 63a216f.

📒 Files selected for processing (4)
  • crates/design-challenge-task/src/emit.rs
  • crates/design-challenge-task/src/lib.rs
  • crates/design-challenge/src/lib.rs
  • crates/design-challenge/src/orchestrator.rs
📝 Walkthrough

Walkthrough

The PR adds sequential design-emission catch-up with pinned blocks, coordination pressure warnings, and backward real-epoch seal retries using chain tempo and bounded walk-back depth.

Changes

Epoch recovery and sealing

Layer / File(s)Summary
Design emission planning and pinned submission
crates/design-challenge/src/lib.rs, crates/design-challenge/src/orchestrator.rs
The emitter plans skipped epochs, delays current-epoch filler leaves until the final 96 blocks, and submits leaves with an explicit epoch and pinned block. Public exports and tests cover the planning behavior.
Coordination pressure reporting
crates/validator-verify/src/coordination.rs, crates/validator/src/epoch_loop.rs
WeightsLatestView now includes an optional metagraph_block. Coordination checks log stale epoch and block conditions while preserving comparison results.
Backward real-epoch sealing
deploy/scripts/prod-real-seal.sh, deploy/AGENTS.md
The sealer reads tempo, tries current and prior epochs within a configurable limit, skips incomplete participant sets, and accepts only HTTP 200 responses.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant emitter_tick
participant design_emit_plan
participant emit_leaves_at
participant ChainState
emitter_tick->>design_emit_plan: request emission plan
design_emit_plan-->>emitter_tick: return epoch and pin_block
emitter_tick->>emit_leaves_at: emit planned leaves
emit_leaves_at->>ChainState: resolve hash at pin_block
ChainState-->>emit_leaves_at: return pinned hash
Loading
sequenceDiagram
participant prod_real_seal
participant ChainStorage
participant SealEndpoint
prod_real_seal->>ChainStorage: read tempo
ChainStorage-->>prod_real_seal: return tempo
prod_real_seal->>SealEndpoint: attempt candidate epoch seal
SealEndpoint-->>prod_real_seal: return HTTP status
prod_real_seal->>SealEndpoint: retry prior epoch when needed
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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: fixing epoch desynchronization between design and real sealing.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/seal-epoch-sync

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/design-challenge/src/orchestrator.rs`:
- Around line 314-327: Replace the catch-up cursor usage around design_emit_plan
and the award_round update at emitted_epoch with a persisted or reconstructable
contiguous next_uncovered_epoch checkpoint. Initialize it from durable coverage
on restart, advance it only after the next contiguous historical epoch is
successfully emitted, and never move it forward for noncontiguous current-epoch
awards. Add tests covering high-epoch restart catch-up and concurrent
award/emitter activity.
In `@crates/validator/src/epoch_loop.rs`:
- Around line 745-783: Update
tick_pressure_verify_allows_match_when_seal_lags_chain to configure the sealed
fixture response with a metagraph_block value below the fake chain’s far-ahead
current_block, ensuring the block-lag validation path executes while preserving
the expected ComparisonOutcome::Match assertions.
🪄 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: 67794d20-f67c-4852-b6be-cb960bd1782a

📥 Commits

Reviewing files that changed from the base of the PR and between 5ec3674 and 2658f73.

📒 Files selected for processing (6)
  • crates/design-challenge/src/lib.rs
  • crates/design-challenge/src/orchestrator.rs
  • crates/validator-verify/src/coordination.rs
  • crates/validator/src/epoch_loop.rs
  • deploy/AGENTS.md
  • deploy/scripts/prod-real-seal.sh

Comment on lines +314 to +327
let last = self.emitted_epoch.load(Ordering::Relaxed);
let Some(plan) = design_emit_plan(
last,
epoch,
state.blocks_since_last_step,
tempo,
state.last_epoch_block,
) else {
return Ok(false);
}
self.emit_leaves().await?;
};
// Pin epoch + block from this tick's schedule snapshot — do **not**
// re-read chain inside emit (end-of-epoch flip used to relabel the set
// as E+1 and permanently skip E, starving real-seal with D24 409s).
self.emit_leaves_at(plan.epoch, plan.pin_block).await?;

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 a contiguous catch-up checkpoint.

emitted_epoch starts at 0 in Orchestrator::new, but line 314 uses it as the catch-up cursor. After a restart at a high chain epoch, the emitter starts at epoch 1 and needs one tick per historical epoch before it reaches the recent gap.

fetch_max also cannot represent contiguous coverage. If award_round emits the current epoch while catch-up is processing an older epoch, line 1148 advances the value to the current epoch. The next tick then skips every remaining missing epoch.

Persist or reconstruct a contiguous next_uncovered_epoch checkpoint. Do not advance that checkpoint when a noncontiguous current-epoch emission succeeds. Add restart and concurrent award/emitter tests.

Also applies to: 1148-1148

🤖 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/design-challenge/src/orchestrator.rs` around lines 314 - 327, Replace
the catch-up cursor usage around design_emit_plan and the award_round update at
emitted_epoch with a persisted or reconstructable contiguous
next_uncovered_epoch checkpoint. Initialize it from durable coverage on restart,
advance it only after the next contiguous historical epoch is successfully
emitted, and never move it forward for noncontiguous current-epoch awards. Add
tests covering high-epoch restart catch-up and concurrent award/emitter
activity.

Comment on lines +745 to +783
#[tokio::test]
async fn tick_pressure_verify_allows_match_when_seal_lags_chain() {
// Same metagraph as the sealed fixture, but chain epoch/tip far ahead —
// pressure-verify warns (validator_seal_lag) yet Match must still proceed.
let epoch = 77u64;
let (client, _chain, trust, merkle_root, _) = sealed_match_fixture(epoch).await;
let miner = [0xA1u8; 32];
let chain = FakeChain::new(FakeChainConfig {
current_block: 10_000,
subnet_epoch_index: epoch + 11,
hotkeys: vec![miner.to_vec()],
owner_hotkey: miner.to_vec(),
commit_reveal_enabled: true,
last_epoch_block: 500,
..FakeChainConfig::default()
});
let dedupe = EpochSubmitDedupe::new();
let submit = CoordinationSubmitConfig {
netuid: 1,
hotkey: vec![0xBBu8; 32],
version_key: 3,
epoch_length: 360,
};
let out = coordination_compare_once(&client, &chain, &trust, Some(&submit), &dedupe)
.await
.expect("ok")
.expect("some");
match out {
ComparisonOutcome::Match {
epoch: e,
merkle_root: root,
..
} => {
assert_eq!(e, epoch);
assert_eq!(root, merkle_root);
}
other => panic!("expected Match despite seal lag, got {other:?}"),
}
}

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

Cover the stale metagraph_block branch.

The fixture response omits metagraph_block, so this test verifies epoch lag only. Add a sealed metagraph_block value below the far-ahead tip so the new block-lag path executes while ComparisonOutcome::Match remains unchanged.

🤖 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/validator/src/epoch_loop.rs` around lines 745 - 783, Update
tick_pressure_verify_allows_match_when_seal_lags_chain to configure the sealed
fixture response with a metagraph_block value below the fake chain’s far-ahead
current_block, ensuring the block-lag validation path executes while preserving
the expected ComparisonOutcome::Match assertions.

Keep pinned-epoch emit + catch-up scheduling while landing under the
design-challenge 1500 LOC gate so CI can merge the seal sync fix.
@echobt
echobt merged commit 09295ff into mainAug 10, 2026
3 checks passed
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