mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
An entity edited after a CORD-06 Refounding was frozen at its pre-Refounding version and its newer state silently discarded on every refold. Observed on device: entity e83ee182 of a real community held at v1 across 21 consecutive refolds while the current epoch offered v2 citing v1's own hash. `EditionFold.foldEntity` anchored the walk by requiring the offered set to *contain* the floor edition. But a Refounding re-wraps ONE edition per entity (CORD-06 §3, "the last Control Plane state is simply rewrapped"), so an entity edited afterwards has only its successor on the new epoch while the floor edition stays behind on the old one. `prev == floor.hash` is a stronger proof the chain connects than mere presence, and it was being ignored. Worse, the gap made `admissible` — the pre-filter every derived fold shares — keep only `version < floor.version`, so the newer edition was dropped every refold: a role edit, channel rename or banlist entry reverting itself for that user. Checked against Armada (semantics only, AGPLv3) and the canonical spec before changing consensus behaviour. Armada selects an arm per entity, and Amethyst was missing both halves: 1. Its chain-walk arm has three anchor branches to our one; the missing `versions[0] === floor + 1n -> bytesEq(lo.prevHash, floorHash)` is now implemented. 2. Once an entity has been re-wrapped into the epoch being folded it does not chain-walk at all — it anchors on version alone (`bootstrapHead`), because behind a compaction dangling `prev`s are normal and, since seal signatures survive re-wrap, any group-key holder can re-serve a genuine old edition under the current group. A re-wrap cannot raise the version inside the signed seal, so version is what bounds that. This is the half that fixes the observed pin; branch 1 alone would still refuse a floor-v1 entity whose only served edition is v3. Implemented as an optional `snapshot` (the rumor ids of the epoch being folded), defaulting to null = pure chain walk for every other caller. `ConcordCommunityState.fold`/`authorizedHeads` capture it from their own editions argument before `admissible` re-seats older-epoch heads — Amethyst folds exactly one epoch per call, so the argument is the snapshot. `admissible` and `candidates` now ask `foldEntity` whether it gapped rather than re-deriving the test, so the three sites cannot drift apart again. `LOG_GAP` also dedups on (entity, floor, offered): the same refusal was re-reported on every refold, 22 byte-identical warnings a boot, which read as 22 attacks rather than one unchanged state. Deduping is what made the two genuinely distinct refusals visible and led here. Fails open above 4096 distinct refusals — a flood is when the warnings matter most. Anti-rollback is unchanged: all 12 pre-existing floor tests still pass, and three new negative tests cover the fork, the below-floor re-serve, and an entity absent from the snapshot. Device gaps for the pinned entity: 21 -> 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>