-
Notifications
You must be signed in to change notification settings - Fork 16
fix(design): cold-start emit current epoch #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,10 +16,16 @@ pub struct DesignEmitPlan { | |
| pub pin_block: u64, | ||
| } | ||
|
|
||
| /// Max epochs to walk back on catch-up (Finney public RPC prunes ~256 blocks). | ||
| const MAX_CATCHUP_EPOCHS: u64 = 16; | ||
|
|
||
| /// Decide whether/which epoch the design filler should emit. | ||
| /// | ||
| /// - Catch up `last_emitted+1` when behind by more than one epoch (repairs the | ||
| /// end-of-epoch relabel race that skipped alternate epochs in prod). | ||
| /// - **Cold start** (`last_emitted == 0`): emit the **current** epoch immediately. | ||
| /// The in-process cursor resets to 0 on every process boot; walking from | ||
| /// epoch 1 pins a pruned block and fails with `SubnetOwnerHotkey not found`. | ||
| /// - Catch up `last_emitted+1` when behind (capped to [`MAX_CATCHUP_EPOCHS`]) | ||
| /// so end-of-epoch relabel skips can recover without exceeding prune depth. | ||
| /// - Otherwise wait until the last [`DESIGN_EMIT_LATE_BLOCKS`] of the current | ||
| /// epoch so admin awards can submit Score leaves first. | ||
| #[must_use] | ||
|
|
@@ -34,12 +40,24 @@ pub fn design_emit_plan( | |
| return None; | ||
| } | ||
| let tempo = tempo.max(1); | ||
| // Cold start after deploy/restart: do not catch up from epoch 1. | ||
| if last_emitted == 0 { | ||
| return Some(DesignEmitPlan { | ||
| epoch: current_epoch, | ||
| pin_block: current_last_epoch_block, | ||
| }); | ||
| } | ||
| if last_emitted >= current_epoch { | ||
| return None; | ||
| } | ||
| // Sequential catch-up for skipped epochs (award path / boundary race). | ||
| if last_emitted + 1 < current_epoch { | ||
| let target = last_emitted + 1; | ||
| let gap = current_epoch.saturating_sub(last_emitted); | ||
| let target = if gap > MAX_CATCHUP_EPOCHS { | ||
| current_epoch.saturating_sub(MAX_CATCHUP_EPOCHS) | ||
| } else { | ||
| last_emitted + 1 | ||
| }; | ||
| let epochs_back = current_epoch.saturating_sub(target); | ||
| let pin_block = current_last_epoch_block.saturating_sub(epochs_back.saturating_mul(tempo)); | ||
| return Some(DesignEmitPlan { | ||
|
|
@@ -82,6 +100,25 @@ mod tests { | |
| assert_eq!(p.pin_block, 8_815_687 - (24423 - 24413) * 360); | ||
| } | ||
|
|
||
| #[test] | ||
| fn emit_plan_cold_start_emits_current_immediately() { | ||
| let p = design_emit_plan(0, 24424, 10, 360, 8_816_047).unwrap(); | ||
| assert_eq!( | ||
| p, | ||
| DesignEmitPlan { | ||
| epoch: 24424, | ||
| pin_block: 8_816_047 | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn emit_plan_caps_catchup_to_prune_window() { | ||
| let p = design_emit_plan(100, 24424, 10, 360, 8_816_047).unwrap(); | ||
| assert_eq!(p.epoch, 24424 - 16); | ||
| assert_eq!(p.pin_block, 8_816_047 - 16 * 360); | ||
| } | ||
|
Comment on lines
+103
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- file inventory ---'
git ls-files 'crates/design-challenge-task/src/emit.rs' 'crates/design-challenge/src/lib.rs' 'crates/design-challenge*' | sed -n '1,160p'
printf '%s\n' '--- outlines ---'
ast-grep outline crates/design-challenge-task/src/emit.rs --lang rust 2>/dev/null | sed -n '1,220p'
ast-grep outline crates/design-challenge/src/lib.rs --lang rust 2>/dev/null | sed -n '1,220p'
printf '%s\n' '--- relevant symbols and assertions ---'
rg -n -C 3 \
'design_emit_plan|DesignEmitPlan|emit_plan|sealed|seal|raw weight|raw_weight|leaf|failure probe|challenge-specific|intake|submit' \
crates/design-challenge-task/src/emit.rs \
crates/design-challenge/src/lib.rs \
crates/design-challenge-task \
crates/design-challenge \
2>/dev/null | sed -n '1,320p'Repository: BaseIntelligence/base Length of output: 24732 🏁 Script executed: #!/bin/bash
set -eu
for f in \
crates/design-challenge/tests/e2e_sim.rs \
crates/design-challenge/src/orchestrator.rs \
crates/design-challenge/src/host_sim.rs \
crates/design-challenge/src/lib.rs
do
if [ -f "$f" ]; then
printf '\n--- %s (%s lines) ---\n' "$f" "$(wc -l < "$f")"
ast-grep outline "$f" --lang rust 2>/dev/null | sed -n '1,260p'
fi
done
printf '\n--- test names and flow terms ---\n'
rg -n -C 4 \
'#\[test\]|#\[tokio::test\]|failure|probe|intake|validate|leaf|weight|seal|sealed|emit_signed_leaf_set|submit_signed_leaf_set|GatewayClient' \
crates/design-challenge/tests crates/design-challenge/src crates/challenge-common \
2>/dev/null | sed -n '1,520p'
printf '\n--- shared helper locations ---\n'
rg -n -C 3 \
'pub (async )?fn (emit_signed_leaf_set|submit_signed_leaf_set)|struct GatewayClient|fn seal|sealed' \
crates 2>/dev/null | sed -n '1,360p'Repository: BaseIntelligence/base Length of output: 50377 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- e2e_sim.rs ---'
cat -n crates/design-challenge/tests/e2e_sim.rs
printf '%s\n' '--- orchestrator emitter and execution ranges ---'
sed -n '500,760p' crates/design-challenge/src/orchestrator.rs | nl -ba
sed -n '760,960p' crates/design-challenge/src/orchestrator.rs | nl -ba
sed -n '960,1110p' crates/design-challenge/src/orchestrator.rs | nl -ba
printf '%s\n' '--- shared submit implementation and tests ---'
cat -n crates/challenge-common/src/submit.rs | sed -n '1,360p'
printf '%s\n' '--- leaf emit implementation and tests ---'
cat -n crates/challenge-common/src/leaf_emit.rs | sed -n '1,260p'
printf '%s\n' '--- gateway sealer routes and tests ---'
rg -n -C 8 \
'Router|route|seal|sealed|raw|weight|test|#\[tokio::test\]|#\[test\]' \
crates/gateway/src/sealer.rs crates/gateway/src/api.rs crates/gateway/tests \
2>/dev/null | sed -n '1,520p'
printf '%s\n' '--- challenge flow markers in tests ---'
python3 - <<'PY'
from pathlib import Path
files = list(Path("crates/design-challenge").rglob("*.rs"))
terms = ("failure", "probe", "intake", "validate", "emit_signed_leaf_set",
"submit_signed_leaf_set", "raw", "seal", "sealed")
for p in files:
text = p.read_text()
hits = [t for t in terms if t in text.lower()]
if hits:
print(f"{p}: {', '.join(hits)}")
PYRepository: BaseIntelligence/base Length of output: 4807 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- orchestrator emitter methods ---'
sed -n '700,930p' crates/design-challenge/src/orchestrator.rs
sed -n '930,1110p' crates/design-challenge/src/orchestrator.rs
printf '%s\n' '--- submit API ---'
sed -n '1,340p' crates/challenge-common/src/submit.rs
printf '%s\n' '--- leaf emit API ---'
sed -n '1,180p' crates/challenge-common/src/leaf_emit.rs
printf '%s\n' '--- sealer relevant symbols ---'
rg -n -C 6 \
'pub fn|pub async fn|Router|route|seal|sealed|raw|weight|#\[test\]|#\[tokio::test\]' \
crates/gateway/src/sealer.rs crates/gateway/src/api.rs crates/gateway/tests 2>/dev/null | sed -n '1,500p'
printf '%s\n' '--- all test source flow markers ---'
rg -n -C 2 \
'emit_signed_leaf_set|submit_signed_leaf_set|sealed\s*[:=]|sealed.*true|seal_epoch|seal|raw_weights|raw weight|failure probe|failure_probe|challenge.*valid|validate.*challenge' \
crates/*/tests crates/*/src 2>/dev/null | sed -n '1,500p'
printf '%s\n' '--- source-level verifier ---'
python3 - <<'PY'
from pathlib import Path
roots = [Path("crates/design-challenge"), Path("crates/challenge-common"), Path("crates/gateway")]
files = [p for root in roots for p in root.rglob("*.rs")]
required = {
"intake": ("insert_harness", "insert_run", "GatewayClient"),
"failure probes": ("failure", "reject", "invalid"),
"challenge validation": ("validate_bundle", "challenge_id", "verify"),
"leaf emission": ("emit_signed_leaf_set", "make_signed_leaf"),
"raw weight submission": ("submit_signed_leaf_set", "RawWeight"),
"sealing": ("seal", "sealed"),
"sealed true assertion": ("sealed: true", "sealed == true", "assert!(sealed"),
}
for p in files:
text = p.read_text()
lower = text.lower()
hits = [name for name, needles in required.items()
if any(n.lower() in lower for n in needles)]
if hits:
print(f"{p}: {', '.join(hits)}")
PYRepository: BaseIntelligence/base Length of output: 50377 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- gateway files ---'
git ls-files 'crates/gateway' | sed -n '1,240p'
printf '%s\n' '--- seal and raw-weight endpoints across gateway ---'
rg -n -C 5 \
'seal_epoch|admin.*seal|/v1/admin|weights/raw|post_raw|RawWeight|sealed|build_latest|build_burn_fallback' \
crates/gateway --glob '*.rs' | sed -n '1,620p'
printf '%s\n' '--- exact flow assertions in tests ---'
rg -n -C 5 \
'#\[test\]|#\[tokio::test\]|assert[^;]*(202|409|sealed|true|raw|seal)|failure|probe|invalid|reject|challenge_id|emit_signed_leaf_set|submit_signed_leaf_set' \
crates/gateway crates/challenge-common crates/design-challenge \
--glob '*test*.rs' --glob 'tests/*.rs' --glob '*.rs' | sed -n '1,700p'
printf '%s\n' '--- focused source-level test verifier ---'
python3 - <<'PY'
from pathlib import Path
roots = [Path("crates/gateway"), Path("crates/challenge-common"), Path("crates/design-challenge")]
for p in sorted({p for root in roots for p in root.rglob("*.rs")}):
text = p.read_text()
if "#[test]" not in text and "#[tokio::test]" not in text:
continue
low = text.lower()
groups = {
"intake": ["insert_harness", "insert_run", "post(\"/v1", "router"],
"failure probes": ["failure", "probe", "invalid", "reject"],
"challenge validation": ["validate_bundle", "challenge_id", "verify"],
"leaf emission": ["emit_signed_leaf_set", "make_signed_leaf"],
"raw submission": ["submit_signed_leaf_set", "/v1/weights/raw"],
"sealing": ["seal_epoch", "admin/seal", "build_sealed_bundle"],
"sealed assertion": ["sealed: true", '"sealed": true', "sealed == true"],
}
present = [name for name, needles in groups.items()
if any(n.lower() in low for n in needles)]
if present:
print(f"{p}: {'; '.join(present)}")
PYRepository: BaseIntelligence/base Length of output: 50377 Add an end-to-end challenge submission test. These tests assert only 📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| #[test] | ||
| fn emit_plan_noop_when_already_emitted_current() { | ||
| assert!(design_emit_plan(11, 11, 350, 360, 1000).is_none()); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Calculate the catch-up limit from block retention.
MAX_CATCHUP_EPOCHSdoes not bound the age ofpin_block. With the new test valuetempo = 360,current_epoch - 16produces a pin block 5,760 blocks old. This exceeds the approximately 256-block retention stated at line 19, so the RPC can still reject the planned emission.Cap
epochs_backby the supported pin-block age divided bytempo. If one epoch exceeds that age, do not select a historical epoch. Update the cap test to enforce the block-age invariant.🤖 Prompt for AI Agents