Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions chain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -284,13 +284,13 @@ ops_gpu_cooldown_seconds = 10800
# operators (a validator on a stale value silently picks a different snapshot).
# NOT part of contract_digest ([training] only). The DEC-CA-0045 rollover
# switches it to [activation] epoch_blocks_after.
epoch_blocks = 3600 # ≈12h at a 12s block time
epoch_blocks = 900 # DECIDED rollover (rolling-era-king, lock 9133200 → 9136800): 3600 → 900 (3h)
round_hours = 12.0 # informational: wall-clock span of an epoch
# Scheduled switch (0/0 = steady state). Last used: 7200 → 3600 at 8726400,
# 2026-07-29. To schedule another, the block must be a boundary of BOTH grids
# (load-checked); set both back to 0 once it is safely past.
epoch_blocks_prev = 0
epoch_activation_block = 0
epoch_blocks_prev = 3600 # DECIDED rollover typed (see rolling_from_block)
epoch_activation_block = 9136800 # DECIDED rollover typed (see rolling_from_block)
# Rolling intake + era king (DEC-CA-0043, block-gated). Trainer POLICY: from
# this block challengers train the moment they are funded, legs cross
# boundaries, every boundary publishes ONE settlement manifest (era king +
Expand All @@ -305,7 +305,11 @@ epoch_activation_block = 0
# the six external validators run a release carrying the era envelope.
# With every key at 0 the block is DECIDED ON CHAIN by validator stake
# ([activation] below, DEC-CA-0045); typing a block here overrides that.
rolling_from_block = 0
# DECIDED 2026-10-31 (lock-in 9133200 → 9136800, the block the trainer's
# published 900-block settlements have followed since). Typed 2026-11-13 so a
# restarting node needs no activation history or archive RPC; a node whose own
# record says otherwise follows this typed value (configured_rollover).
rolling_from_block = 9136800
# Settlements per era (consecutive boundaries sharing one set of training
# seeds, one init and one cached king checkpoint): 4 on the 3h grid = 12h
# eras — today's king-leg cadence with 3h verdicts inside it.
Expand Down Expand Up @@ -692,7 +696,7 @@ increment_from_block = 9046800 # = cohort_maxt_from_block (both flip at the eve
# Do NOT type "v2" / a block after the rollover here: it breaks that ordering
# (apply_activation refuses it) and would move live pre-v2 era-king
# settlements back to level units (2026-10-20 review; DEC-CA-0039 note).
cohort_maxt_increment_from_block = 0
cohort_maxt_increment_from_block = 9136800 # DECIDED rollover typed (what apply_activation sets)
# Multivariate scoring activation (DEC-CA-0041): from this block a multivariate
# window's channels are averaged into ONE per-window contribution (GIFT-Eval
# weighting — the window counts once, not once per channel), so the round point
Expand Down Expand Up @@ -742,12 +746,12 @@ margin_v2_from_block = 9194400 # TYPED 2026-11-01 (DEC-CA-0067): = the bloc
# pinned). Must equal [round] rolling_from_block and be >=
# cohort_maxt_increment_from_block >= cohort_maxt_from_block (load-checked).
# 0 = off (bit-identical to the ungated validator).
era_king_from_block = 0
era_king_from_block = 9136800 # DECIDED rollover typed
# Tenure / ripeness in BLOCKS from the rollover (CONSENSUS): the margin decay
# and the promotion clock keep their wall-time length across the 3600 → 900
# grid switch instead of running 4× faster. Both *_blocks values are inert
# until tenure_blocks_from_block is set (= ROLLOVER).
tenure_blocks_from_block = 0
tenure_blocks_from_block = 9136800 # DECIDED rollover typed
margin_warmup_blocks = 28800 # = 8 rounds × 3600 blocks (~4 d) — same decay as today
cascade_reign_blocks = 18000 # = 5 rounds × 3600 blocks (~2.5 d) — same ripeness as today
king_resync_max_blocks = 18000 # = king_resync_max_rounds (5) × 3600 blocks — same valve as today
Expand Down Expand Up @@ -1123,7 +1127,9 @@ dormant_after_blocks = 0
[v2]
launch_at_utc = "2026-10-30T12:00:00Z"
commit_open_at_utc = "2026-10-30T09:00:00Z" # V2 reveals queue here; training waits for v2
commit_from_block = 0 # resolved from finalized timestamps, or explicitly pinned by operator
# Pinned 2026-11-13: block 9395601 is the first finalized block at/after
# commit_open_at_utc (stamped 2026-10-30T09:00:00Z; archive read).
commit_from_block = 9395601
prelaunch_burn_readiness = "r2"
# cascade v2 (DEC-CA-0050): Cascade-MOE-28M king lineage (lane A) + the
# domain-expert lane (lane B). Finalized chain timestamps resolve launch_at_utc
Expand All @@ -1135,7 +1141,10 @@ prelaunch_burn_readiness = "r2"
# router bites once the routing gate opens, receipts stamp `v2_block`, and
# lane A keeps 100 % until another lane launches. Planned order: C first
# (A/C 75/25), then B (A/B/C 50/25/25); activation remains separate for each.
from_block = 0
# Pinned 2026-11-13 = what launch_at_utc resolved to: the 12:00:00Z crossing is
# block 9396501, the next era start on the decided grid is 9136800 + 73×3600.
# With both blocks typed a restart reads no historical timestamps (no archive).
from_block = 9399600
# Rides the same go-live (DEC-CA-0054): every v2 settlement enforces the
# bench gate (the mode; the RULE is DEC-CA-0054's three suites + six-number
# geomean only until bench_gate_v2_from_block below, which on mainnet is the
Expand Down Expand Up @@ -1201,6 +1210,11 @@ reign_warm_start_blocks = 21600
# to burn_uid (miners keep their split of the rest); it ends at the first v2
# settlement. prelaunch_burn_from_block = <block> types it instead (override).
prelaunch_burn = 0.95
# DECIDED (prelaunch-95-r2: lock 9486000 → 9486900, the block every node seen
# carries); typed 2026-11-13 (owner go). Behaviour-identical: the burn applies
# only before the first v2 settlement (emission_policy), and v2 is active from
# 9399600 < 9486900, so it never applied — typing frees the note segment.
prelaunch_burn_from_block = 9486900
# The carried (toto2) king's court is paid at most 5 % of total emissions from
# the v2 block until a king is crowned at a v2 settlement (lane B unchanged).
carryover_king_share = 0.05
Expand Down
13 changes: 9 additions & 4 deletions decided_votes.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ feature = "rolling-era-king"
lock_block = 9133200
activation_block = 9136800
source = "config"
type_it = false
reason = "nodes resolved it differently: the operator node 9133200→9136800, one external validator 9374400→9378000 (it restarted late and adopted other notes). Typing either value in main would override a block some nodes decided; the operator types its own on its deployed file only."
# typed in chain.toml 2026-11-13 (owner decision): 9136800 is the block the
# trainer's published 900-block settlements have followed since. One external
# validator recorded 9374400→9378000 (it restarted late and adopted other
# notes); the grids coincide from 9378000 on, so its live scoring is unchanged
# and the typed value wins over its record (configured_rollover).

[[vote]]
feature = "forfeit-42b1a486"
Expand All @@ -47,8 +50,10 @@ feature = "prelaunch-95-r2"
lock_block = 9486000
activation_block = 9486900
source = "tally"
type_it = false
reason = "HELD (owner decision pending, 2026-11-13): decided and applied on every node seen (operator + the external validator's note both carry 9486000:9486900); typing [v2] prelaunch_burn_from_block = 9486900 is behaviour-identical and frees another note segment, but it is a release chain.toml edit the owner has not yet approved. Next one-line PR."
# typed in chain.toml 2026-11-13 (owner go): decided on every node seen
# (operator + the external validator's note both carry 9486000:9486900).
# Behaviour-identical: the burn applies only before the first v2 settlement
# and v2 is active from 9399600, so it never applied.

[[vote]]
feature = "v2b-69630f92"
Expand Down
2 changes: 1 addition & 1 deletion docs/AGENT_MINING.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ deployment or qualification succeeded:
<!-- cascade:config-reference:start -->
| Template | V2 block | Loaded B gate | C / MarketCast blocks | Random-init eras | B pool | CPU generation |
|---|---:|---:|---|---:|---|---|
| `chain.toml` | 0 | 0 | 0 / 0 | 14 | 25% | enabled for shards; 90 min configured |
| `chain.toml` | 9399600 | 0 | 0 / 0 | 14 | 25% | enabled for shards; 90 min configured |
| `chain.testnet.toml` | 8190000 | 8190001 | 0 / 0 | 0 | 50% | off; 90 min configured |
<!-- cascade:config-reference:end -->

Expand Down
11 changes: 9 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def isolated(runner):


@pytest.fixture(scope="session")
def cfg() -> ChainConfig:
def cfg(tmp_path_factory) -> ChainConfig:
"""The mainnet template with its ENFORCING pins neutralized.
chain.toml carries the real launch pins (expected_gpu, the worker-image
Expand All @@ -122,7 +122,14 @@ def cfg() -> ChainConfig:
"""
from dataclasses import replace

c = load_chain_config(REPO_ROOT / "chain.toml")
from tests.unit.predecision_main import predecision_path

# The release TYPES the decided rollover (9136800) and the resolved v2
# launch (9399600) since 2026-11-13 — real-chain state, like the pins.
# Fixtures run on tiny fake block clocks, so they start from the release
# file with those blocks untyped (the vote/launch undecided), exactly the
# file every fixture ran on before. Shipped-file facts: load chain.toml.
c = load_chain_config(predecision_path(tmp_path_factory.mktemp("predecision")))
# Tests using fake block clocks also reparse raw configuration (satellites,
# overlays). Neutralize the real calendar there as well as on cfg.v2.
import copy
Expand Down
63 changes: 63 additions & 0 deletions tests/unit/predecision_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""The release chain.toml as it shipped BEFORE 2026-11-13 typed the decided
rollover and the resolved v2 launch: every DEC-CA-0043 rollover key back to
its vote-open value and ``[v2] from_block`` / ``commit_from_block`` back to 0.
Tests that exercise the on-chain rollover vote or the dated-launch resolution
from a fresh node's point of view load this; tests about the shipped release
load ``chain.toml`` itself. ``test_predecision_main_is_the_exact_inverse``
pins that this is the exact inverse of the typed lines — and that applying the
decided rollover + resolving the launch lands back on the shipped config.
"""
from __future__ import annotations

import re
from pathlib import Path

REPO = Path(__file__).resolve().parents[2]

# (section, key, pre-decision value) — exactly the lines typed on 2026-11-13.
UNTYPED = (
("round", "epoch_blocks", "3600"),
("round", "epoch_blocks_prev", "0"),
("round", "epoch_activation_block", "0"),
("round", "rolling_from_block", "0"),
("scoring", "cohort_maxt_increment_from_block", "0"),
("scoring", "era_king_from_block", "0"),
("scoring", "tenure_blocks_from_block", "0"),
("v2", "commit_from_block", "0"),
("v2", "from_block", "0"),
# absent before (the vote decided it): None = delete the typed line
("v2", "prelaunch_burn_from_block", None),
)


def predecision_text(text: str | None = None) -> str:
"""``chain.toml`` text with the decided blocks untyped (section-scoped:
only the key inside its own table is rewritten)."""
text = (REPO / "chain.toml").read_text() if text is None else text
for section, key, value in UNTYPED:
head = re.search(rf"(?m)^\[{re.escape(section)}\]\s*$", text)
assert head, section
nxt = re.search(r"(?m)^\[", text[head.end():])
end = head.end() + (nxt.start() if nxt else len(text) - head.end())
if value is None:
body, n = re.subn(rf"(?m)^{re.escape(key)}\s*=[^\n]*\n", "",
text[head.end():end], count=1)
else:
body, n = re.subn(rf"(?m)^{re.escape(key)}(\s*)=\s*[^\n#]*?(\s*#.*)?$",
rf"{key}\g<1>= {value}", text[head.end():end], count=1)
assert n == 1, (section, key)
text = text[:head.end()] + body + text[end:]
return text


def predecision_path(tmp_path) -> Path:
p = Path(tmp_path) / "chain.predecision.toml"
p.write_text(predecision_text())
return p


def predecision_main(tmp_path):
from cascade.shared.config import load_chain_config

return load_chain_config(predecision_path(tmp_path))
26 changes: 18 additions & 8 deletions tests/unit/test_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import cascade.shared.activation as A
from cascade.shared.config import load_chain_config
from cascade.shared.era import era_king_active, rolling_active, tenure_blocks_active
from tests.unit.predecision_main import predecision_main, predecision_text

REPO = Path(__file__).resolve().parents[2]
FEATURE = "rolling-era-king"
Expand All @@ -27,11 +28,13 @@


@pytest.fixture
def cfg():
"""The shipped chain.toml with its DEC-CA-0048 forfeiture, DEC-CA-0049
margin-v2 bar and DEC-CA-0055 pre-launch burn DISARMED: these tests model
the plain fleet (none configured) and arm one explicitly."""
c = load_chain_config(REPO / "chain.toml")
def cfg(tmp_path_factory):
"""The release chain.toml as it shipped BEFORE the decided rollover was
typed (the rollover still open to the stake vote — what these tests
exercise), with its DEC-CA-0048 forfeiture, DEC-CA-0049 margin-v2 bar and
DEC-CA-0055 pre-launch burn DISARMED: these tests model the plain fleet
(none configured) and arm one explicitly."""
c = predecision_main(tmp_path_factory.mktemp("predecision"))
return replace(c, scoring=replace(c.scoring, forfeit_hotkeys=(), forfeit_from_block=0,
forfeit_successor_hotkey="", win_margin_start_v2=0.0,
win_margin_end_v2=0.0, margin_warmup_blocks_v2=0,
Expand Down Expand Up @@ -383,17 +386,24 @@ def test_startup_activation_arms_from_the_notes_and_persists(cfg, tmp_path):
# ── config loading ───────────────────────────────────────────────────────────


def test_shipped_configs_carry_the_activation_section():
def test_shipped_configs_carry_the_activation_section(tmp_path):
main = load_chain_config(REPO / "chain.toml")
assert main.activation.enabled and main.activation.feature == FEATURE
assert main.activation.threshold == 0.51 and main.activation.epoch_blocks_after == 900
assert main.round.rolling_from_block == 0 # the fleet decides the block
# the fleet's decided rollover is typed into the release (2026-11-13)
assert main.round.rolling_from_block == 9136800
assert A.configured_rollover(main) == 9136800
# before it was typed the release left the block to the fleet's vote
pre = predecision_main(tmp_path)
assert pre.activation.enabled and pre.activation.feature == FEATURE
assert pre.round.rolling_from_block == 0 and A.configured_rollover(pre) == 0
test = load_chain_config(REPO / "chain.testnet.toml")
assert test.activation.enabled and A.configured_rollover(test) == 600 # typed in wins


def test_loader_rejects_a_bad_threshold_or_grid(tmp_path):
src = (REPO / "chain.toml").read_text(encoding="utf-8")
# the pre-decision release: the grid checks bite on an untyped rollover
src = predecision_text()
p = tmp_path / "chain.toml"
p.write_text(src.replace("threshold = 0.51", "threshold = 1.5"), encoding="utf-8")
with pytest.raises(ValueError, match="threshold"):
Expand Down
8 changes: 6 additions & 2 deletions tests/unit/test_bench_gate_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,15 @@ def test_parse_key():
parse_bench_gate_v2_from_block({"bench_gate_v2_from_block": bad})


def test_shipped_configs():
def test_shipped_configs(tmp_path):
from cascade.shared.config import load_chain_config
from tests.unit.predecision_main import predecision_main

main = load_chain_config("chain.toml")
assert main.v2.from_block == 0 and main.v2.bench_gate_v2_from_block == 0
# 2026-11-13: main types the resolved v2 launch; the gate's "v2" follows it
assert main.v2.from_block == 9399600 and main.v2.bench_gate_v2_from_block == 9399600
before = predecision_main(tmp_path)
assert before.v2.from_block == 0 and before.v2.bench_gate_v2_from_block == 0
assert main.scoring.bench_gate_tolerance == 0.01
assert main.scoring.bench_gate_min_win_rate == 0.5
test = load_chain_config("chain.testnet.toml")
Expand Down
Loading
Loading