Skip to content

feat(chain): revert by release machinery, empty table — §5.6 rules, ASERT re-anchor, upgrade-required, --revert drill (P9b, D19) - #13

Merged
cyberia-apparatus merged 4 commits into
release/phase1from
feat/p9b-revert-machinery
Nov 2, 2026
Merged

cyberia-apparatus merged 4 commits into
release/phase1from
feat/p9b-revert-machinery

Conversation

@cyberia-apparatus

Copy link
Copy Markdown
Collaborator

What

P9b of the director's plan (soft3/release/director/plan.md, D19). This PR adds the machinery of revert by release, specs/chain.md §5.6 as drafted in #12, with an empty compiled table (params::REVERTS = &[]). The spec text stays in #12 and is the owner's decision (D6). This PR has no spec text.

  • params.rs: Revert and REVERTS sit beside ACTIVATIONS. revert.rs holds the effective table: the compiled entries plus --revert rehearsal entries. Its static checks are sort order, a non-empty vetoed, rule sets that strictly increase, and no keep that an earlier entry vetoes. Its store checks are the ancestry of keep and that each vetoed id lies above H. An inconsistent table refuses to open.
  • Validation (engine.rs check_header, engine_api.rs accept_header):
    • keep acts as a compiled checkpoint at H.
    • Vetoed ids and their descendants are refused without a body and without a score.
    • The block at H+1 must have time ≥ anchor_time.
    • The rule set from H+1 on is the entry's.
  • ASERT re-anchor (daa.rs next_bits_reverted): for h > H+1, Δt = parent.time − anchor_time, ideal = (h−H−2)·T, and the anchor target is target(bits_{H+1}). With no entry below the parent it is next_bits, unchanged.
  • upgrade-required (§7.1): a header with valid PoW whose rule set is above the last known one sets the flag and is neither stored nor relayed. UnknownRuleSet now scores 0, because a peer of another release is not an attacker (chain-wire §5).
  • Open-time rollback (Chain::apply_reverts, run before replay()): it marks the vetoed blocks, their descendants and any non-keep block at H as invalid, then cuts the main chain below the first invalid block. The state is rebuilt by the existing replay from genesis with its root checks, which is the spec's wipe-and-replay path. Retracted transactions, coinbases skipped, are re-admitted through Mempool::admit at node start, so only those still valid stay.
  • Event::Reverted { to_height, keep, vetoed, rehearsal } is queued before any block event and kept by the kernel's moved(). It is also persisted in <home>/reverted.log.
  • --revert H:keep:vetoed[,…]:anchor_time[:rule_set] is a repeatable rehearsal flag. It is refused on pussy and bostrom (by name, and by a pinned CHAINS id). pussy-rc is allowed, which the alpha-network drill needs.
  • /status gains reverts, reverted, rehearsal and upgrade-required (plus upgrade-height and upgrade-rule-set). activations is now computed and reads 1@0 when the table is empty.

Verified

Not yet. Nothing was compiled on the director's host, by rule. The gate is a soft3 Release train rehearsal with override=cybernode=pull/<this>/head, and its receipts will be posted here. Review notes from the director:

  • With an empty table every changed path takes the old code: rule_set_for = RULE_SET, bits_after = next_bits, apply_reverts returns early, and the template has version 1.
  • ChainNode::open now calls KernelChain::open_with({checkpoints: true, clock: from_env, ..Default}), which is exactly what Chain::open built.
  • The sibling simulation crate builds OpenOptions from soft3::chain::engine, not from cybernode, so the new field cannot break it.

Tests (chain::tests::revert::*):

  • an_empty_table_changes_nothing
  • two_nodes_revert_by_rule_and_end_on_one_tip_above_h
  • an_old_release_node_reports_upgrade_required_and_keeps_its_tip
  • an_inconsistent_table_refuses_to_open
  • a_rehearsal_refuses_to_start_on_a_production_chain
  • asert_reanchors_above_an_entry_in_the_function
  • asert_reanchors_in_the_engine_and_is_unchanged_without_an_entry
  • p9b_timing_undo_and_replay_per_block, which prints p9b-timing undo_per_block_us=… replay_per_block_us=… as the cost receipt §5.6 asks for

There are also five unit tests in chain::revert::tests and cli::tests::node_args_revert_is_repeatable_and_validated.

Remains

Risk

  • wide: this is consensus code. With an empty table the only change in behaviour is that a header of an unknown rule set is unscored and sets upgrade-required, where it used to score 10. At the launch PoW limit, upgrade-required costs little to trigger. It only raises a flag, and the node keeps its tip.
  • The code is uncompiled until the build service has run it. Commit 1 alone does not build its tests, because the new OpenOptions.reverts field reaches tests/mod.rs only in commit 3.

🤖 Generated with Claude Code

cyberia-apparatus and others added 3 commits November 2, 2026 08:14
The release carries a revert table beside ACTIVATIONS, shipped empty.
An entry makes `keep` a compiled checkpoint, vetoes the old block(s) at
H+1 and their descendants, requires time >= anchor_time at H+1 and the
entry's rule set from H+1, and re-anchors ASERT above H on the anchor
time and the target of block H+1 (daa::next_bits_reverted). The table is
checked statically and against the store at open; an inconsistent one
refuses to start. At open the stored main chain is cut below its lowest
invalid block (state is rebuilt by the existing replay, so no undo is
needed), the retracted transactions are kept for the mempool and an
Event::Reverted is queued ahead of any block event and logged. A PoW-valid
header of an unknown rule set sets upgrade_required and is not stored.
With an empty table every path reduces to the previous code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…her releases

`--revert H:keep:vetoed[,..]:anchor_time[:rule_set]` adds rehearsal
entries (rule set defaults to the last known + 1); the engine refuses
them on pussy and bostrom. /status gains reverts, reverted, rehearsal and
upgrade-required (activations now lists the revert rule sets). Peers
offering vetoed headers or headers of an unknown rule set are no longer
scored (chain-wire §5); the node re-admits retracted transactions to the
mempool at start, and the wire adapter keeps Reverted events queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…SERT, timing

Covers the empty table, two nodes reaching one tip above H by rule, an
old-release node reporting upgrade-required and keeping its tip, the
inconsistent-table and production-chain refusals, the ASERT re-anchor
(function and engine), and a p9b-timing receipt line for undo and replay
cost per block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cyberia-apparatus cyberia-apparatus added the wide lane: wide — contract, shared crate, release line (deliver.md § lanes) label Nov 2, 2026
@cyberia-apparatus

Copy link
Copy Markdown
Collaborator Author

Rehearsal 41411831611 does not measure this PR — void, re-dispatched as 41414017957.

Receipt (sources.json, candidate-x86_64-apple-darwin of 41411831611): the only override was cybernode=pull/13/head (7eae92d). soft3 resolved to main 3045d54 and lens to master 11113c6, not to the stand. So every leg failed in package-resolution (failed to load manifest for dependency cyber-lens-gpu … lens/gpu/Cargo.toml: No such file) before any test ran. Nothing in that run is about #13; the dispatch was the director's error (session 544).

41414017957 (candidate-20261102.96, cut=false) uses the override set of baseline 41400293113 with two changes: cybernode=pull/13/head and soft3=2ada471 (current release/phase1). Since the baseline's 7608c82, soft3 differs only in release/director/plan.md and audit/release-2026-11-02/director.md. The baseline's reds that wait on the owner are phase1-pins, release-notes-source, stack-zheng, stack-mudra and stack-neuron. #13 merges if 41414017957 adds no red and cybernode-tests passes the new tests on every leg.

@cyberia-apparatus

Copy link
Copy Markdown
Collaborator Author

Receipt, rehearsal 41414017957: leg 1 of 5 (Linux x86_64)

  • Sources: sources.json takes cybernode at 7eae92d, this PR's head (pull/13/head). soft3 is at 2ada471 and lens at d6dcc3f, the baseline's full override set, so this run builds what the baseline built apart from this PR.
  • cybernode-tests: green, 336 passed, 0 failed. Baseline 41400293113 on the same leg had 323 / 0, and that count includes one test from fix(chain): a corrupted body never invalidates the honest header — §5.5 commitment first (C2a, from soft3#350) #10. Every new chain::revert::* and chain::tests::revert::* test passes, among them two_nodes_revert_by_rule_and_end_on_one_tip_above_h, an_old_release_node_reports_upgrade_required_and_keeps_its_tip, an_empty_table_changes_nothing and a_rehearsal_refuses_to_start_on_a_production_chain.
  • Red gates: phase1-pins and release-notes-source, exactly the baseline's (G8, G9, waiting on the owner). soft3-tests, soft3-release, node-status and linux-glibc-floor are green.
  • Not captured: the test p9b_timing_undo_and_replay_per_block passes, but its timing output is not in the log because the gate runs without --nocapture.

macOS aarch64, macOS x86_64, Linux aarch64 and the stack leg are still running. This PR does not merge until all five are read.

@cyberia-apparatus

Copy link
Copy Markdown
Collaborator Author

Rehearsal 41414017957: legs 2 and 3 of 5 read (director, session 548, 09:1x UTC).

leg job concluded cybernode-tests P9 tests reds
Linux x86_64 (session 546) 08:47:41Z 336 / 0 all revert ok phase1-pins, release-notes-source
Linux aarch64 08:54:38Z 336 / 0 15 revert tests ok, among them two_nodes_revert_by_rule_and_end_on_one_tip_above_h, p9b_timing_undo_and_replay_per_block phase1-pins, release-notes-source
macOS aarch64 08:55:45Z 336 / 0 same 15 ok phase1-pins, release-notes-source
macOS x86_64, stack in_progress

Both legs build from overrides cybernode=pull/13/head (head 7eae92d), soft3=2ada471, lens=d6dcc3f (receipt/sources.json), and soft3-tests is 2 / 0. The failure conclusions come from the baseline reds only. Source: receipt/release-validation.json and receipt/logs/cybernode-tests.txt of artifacts candidate-aarch64-unknown-linux-gnu and candidate-aarch64-apple-darwin.

The design, after the owner's answers of 2026-11-02 (plan P9): the trigger is now a revert statement signed by an owner-held key in the genesis params, not a table compiled into a release. This PR stays the core, for these reasons:

  • RevertTable is already a runtime table: the compiled REVERTS plus the --revert entries.
  • The rewind, the veto of the old H+1, the ASERT re-anchor, Event::Reverted and /status are the same under either trigger.
  • With an empty table, every path still takes the old code.

P9c adds the statement as a third source of entries, and nothing here is reworked. The merge waits on the last two legs: no red beyond the baseline, and the new tests passing.

@cyberia-apparatus

Copy link
Copy Markdown
Collaborator Author

Rehearsal 41414017957, legs 4 and 5 of 5 read (director, session 549). This PR adds one red and does not merge yet.

leg cybernode-tests revert tests reds
macOS x86_64 (job 125173559064, 09:37:39Z) 336 / 0 15 ok, incl. two_nodes_revert_by_rule_and_end_on_one_tip_above_h and p9b_timing_undo_and_replay_per_block phase1-pins, release-notes-source (baseline)
stack (job 125173559176, 09:34:28Z) 336 / 0 15 ok phase1-pins, release-notes-source, stack-zheng, stack-mudra, stack-neuron (baseline) plus simulation-gate

The new red, logs/simulation-gate.txt (sha256 a7627224…a112), exit 101:

error[E0063]: missing field `reverts` in initializer of `soft3::chain::engine::OpenOptions`
   --> chain/src/scenarios.rs:121:15
error[E0063]: missing field `reverts` in initializer of `soft3::chain::engine::OpenOptions`
  --> chain/src/launch.rs:88:15

OpenOptions.reverts is new, and the sibling simulator builds OpenOptions with literals that name every field. Session 544's review said the simulation crate was not affected, and that was wrong.

Fix on the consumer, not here. OpenOptions already derives Default, and this repository's own callers use ..Default::default(). simulation#2 (the stand's simulation input) gains 16abd2d: both literals use ..Default::default() and drop the two default-valued fields. They compile against f1065eb and against this PR, and do not trigger needless_update. Gate: rehearsal 41423712587 (candidate-20261102.97, cut=false) uses 41414017957's full override set with only simulation=16abd2d. Its snapshot is green. This PR merges when that run's stack leg shows simulation-gate green and adds no other red.

@cyberia-apparatus

Copy link
Copy Markdown
Collaborator Author

Correction (director, session 550 — the earlier comment said 549): rehearsal 41423712587 is void, cancelled at its snapshot. It built pull/13/head = 7eae92d, which does not contain cybernode#14 (f26b79c, merged into release/phase1 by session 549). #14 touches cli.rs, chain/mod.rs and node/chain_node.rs, so a gate on 7eae92d would not show what merges. #13 has been brought up to date by a merge commit (no rewrite): head a21ffee, tree aa0e978, the clean git merge-tree of f26b79c and 7eae92d. The gate is now rehearsal 41423930605 (candidate-20261102.98, cut=false, snapshot green): 41414017957's override set with cybernode=a21ffee and simulation=16abd2d. The merge rule for #13 is unchanged.

@cyberia-apparatus

Copy link
Copy Markdown
Collaborator Author

Gate read — rehearsal 41423930605 (candidate-20261102.98, cut=false, sources.json: cybernode a21ffee = this head, simulation 16abd2d = simulation#2, soft3 2ada471, lens d6dcc3f, the stand's full override set).

leg reds cybernode-tests revert tests
Linux x86_64 (job 125206273037) phase1-pins, release-notes-source 336 / 0 15 ok
Linux aarch64 (job 125206272881) phase1-pins, release-notes-source 336 / 0 15 ok
macOS aarch64 phase1-pins, release-notes-source 336 / 0 15 ok
stack (self-hosted, 11:13:06Z) phase1-pins, release-notes-source, stack-zheng, stack-mudra, stack-neuron 336 / 0 15 ok
macOS x86_64 in progress at merge; read 336 / 0, 15 revert ok on the pre-sync head in 41414017957

simulation-gate is green on the stack leg (it was red in 41414017957 with E0063 missing field reverts). stack-zheng (the_shipped_final_proof_is_at_most_64_kib_over_every_transcript, wrap/tests.rs:135), stack-mudra (domain_compat.rs:11) and stack-neuron fail exactly as in the baseline 41414017957; their failure lines differ only in thread ids. No red is added.

Review (director, second pass): base release/phase1 is still f26b79c; the diff against it is the 16 files, +1205 −44, reviewed in session 544, with REVERTS empty. OpenOptions literals: within cybernode they use ..Default::default() or name reverts (tests). No other sibling of the stand names OpenOptions (git grep over soft3 2ada471). Simulation is fixed by simulation#2.

Merging into release/phase1 with --match-head-commit a21ffee.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wide lane: wide — contract, shared crate, release line (deliver.md § lanes)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant