Skip to content

feat(audit): decided-votes drift guard — decided_votes.toml, cascade-audit decided, CI test - #458

Merged
tensorlink-dev merged 2 commits into
mainfrom
feat/decided-votes-drift-guard
Nov 13, 2026
Merged

tensorlink-dev merged 2 commits into
mainfrom
feat/decided-votes-drift-guard

Conversation

@tensorlink-dev

Copy link
Copy Markdown
Contributor

Owner-approved 2026-11-13 04:30 UTC ("Build this"). No runtime behaviour change, no consensus field touched, no restart needed.

Why

Decided on-chain votes (DEC-CA-0045 stake-weighted activation) are meant to be TYPED into the release chain.toml once applied — behaviour-identical, and it frees the feature's segment of the 128-byte readiness note. Until tonight that typing happened only on the operator's deployed file. main lagged for weeks; an external validator that pulled main overflowed its note and refused to start (OPSLOG 2026-11-13 00:13; symptom fixed by #457). Nothing connected "what the fleet decided" to "what main ships". This PR does.

What

  1. decided_votes.toml (beside chain.toml) — the one place in git recording what the fleet decided: each vote's feature name, lock block, activation block, source, and the key that types it; plus the four worker-image pins the network trains on. Rows can be type_it = false (HELD) with a reason: rolling-era-king (nodes resolved it differently — ours 9133200→9136800, one external 9374400→9378000 — so main must not type either), and prelaunch-95-r2 (decided 9486000→9486900 everywhere seen; typing it is behaviour-identical and frees another segment, but it is a release chain.toml edit awaiting the owner's word — next one-line PR).
  2. cascade/shared/decided.py — drift_report(cfg, record): TYPE (decided, applied, not typed → prints the exact [section] / key = block lines), MISMATCH, PIN_DRIFT, STALE (a row whose feature name the config no longer computes = changed vote params = a fresh vote), PENDING (not yet applied as of the record's block — never asked early), HELD. state_report(records, record): a node's activation_*_state.json lock-ins vs the record → UNRECORDED (prints the [[vote]] row to add), DISAGREE, UNKNOWN (retired feature; our box still holds a promotion-carry record).
  3. cascade-audit decided [--config F] [--decided F] [--state-dir D] [--as-of-block N] [--json] — runs both reports, exit 1 on drift. Reads only.
  4. CI guard — tests/unit/test_decided_votes.py::test_repo_chain_toml_and_record_are_in_sync runs drift_report on the repo's own files: a vote that resolves and is not typed (or a pin moved on one side) turns CI red on the next PR, whatever it is about. Plus unit tests for every finding kind and the CLI.
  5. Lock-in log line — the resolver (resolve_activation) now logs DECIDED: <feature> applies at block N — once that block has passed, type \key = N` under [section] in the release chain.toml and record it in decided_votes.toml (PR to main)` at every LOCKED IN. One lazy import; nothing else changes.
  6. docs/AUDIT.md section.

Verified on the box (python -P, config + stdlib only)

  • cascade-audit decided on the repo: in sync (4 OK votes, 2 HELD, 4 OK pins), exit 0.
  • On the deployed file with our state dir: reports exactly the known gap — [TYPE] v2b-69630f92 → [v2] image_decoupled_from_block = 9493200 (to add at the next quiet validator restart), the HELD prelaunch row, [UNKNOWN] promotion-carry (retired feature's state file). Pins OK. Exit 1.
  • ruff clean.

Not in this PR (owner decisions)

  • Typing [v2] prelaunch_burn_from_block = 9486900 in main (HELD row above).
  • Publishing the netconfig overlay (validators would get decided values within 5 min without pulling main) — testnet first, validator restart.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BMT4D7eZUKUF2gocYLVFmj

…audit decided, CI test

Decided on-chain votes must be typed into the release chain.toml once
applied; until now that happened only on the operator's deployed file and
main lagged (an external validator pulling main overflowed its readiness
note, 2026-11-13). This records what the fleet decided in git, compares
chain.toml and a node's lock-ins against it (exact lines to type), fails
CI on drift, and logs a "type this" hint at every lock-in. Reads only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMT4D7eZUKUF2gocYLVFmj
@vercel

vercel Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cascade Ready Ready Preview Nov 13, 2026 4:19am UTC

Request Review

…egardless of the typed block

A typed feature keeps its computed name (the spec carries the typed
block), so a missing name means changed params or a retired vote.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMT4D7eZUKUF2gocYLVFmj
@tensorlink-dev
tensorlink-dev merged commit 90fc92a into main Nov 13, 2026
3 checks passed
@tensorlink-dev
tensorlink-dev deleted the feat/decided-votes-drift-guard branch November 13, 2026 20:50

This branch was successfully deployed

1 active deployment
Preview — 4430fbbd Deployed Nov 13, 2026 by vercel[bot]
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