Skip to content

Release v0.20.1 - #19

Merged
jdogresorg merged 72 commits into
masterfrom
release/v0.20.1
Sep 24, 2026
Merged

jdogresorg merged 72 commits into
masterfrom
release/v0.20.1

Conversation

@jdogresorg

@jdogresorg jdogresorg commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Release v0.20.1

Coordinated platform release across the 13-repo v0.20.1 train.

  • Base: master
  • Head: release/v0.20.1

What changes

  • Re-slides the v0.20.1 testnet activation train from 153221 to 154074, with BTC producer/consumer activation at 154234/154291, DOGE producer/consumer activation at 67942777/67944741, and ANCHOR_ATTEST_BARRIER_ACTIVATION at 154291; LTC:testnet mirror admission remains null under dq4 (a).
  • Checks schema_version on every dispenser reconciliation page, fills the reconciliation controls, and fails closed on block-hash reads.
  • Halts durably on repeating market duplicate-key failures and adds cross-source market-id parity checking.
  • Correctly replicates edited token rows, reconciles unstable index status ids, and honors Litecoin testnet reorg depth and halt status.

Sibling release PRs

Filled in by open-release-prs.sh --execute once every PR above has a number:

…he decoder lifecycle harness uses the shared server helper
Capture the validated source table set during schema reconciliation and report only replicated tables that exist upstream but not locally. Keep unknown source schemas fail-visible and preserve server-side build coverage checks.
… the BTC instant

The v0.20.0 family was sized 2026-09-16 20:41Z from last-99-block cadences. LTC
testnet then ran at about 82 s per block against the 146.6 s that sizing assumed,
pulling its producer boundary to 3.2 h out while BTC's stayed 53.0 h out, and
DOGE's drifted 7.5 h early. Two legs of one cross-chain match would have crossed
the flag day about two days apart, which is what the same-wall-clock-instant rule
exists to prevent.

Re-measured 2026-09-17 22:45Z over a trailing window as long as the lead being
sized: TBTC 152,891 at 576.7 s per block, TLTC 4,889,190 at 82.5 s, TDOGE
67,904,912 at 27.7 s. BTC is unchanged, still keyed to epoch close 153,216 plus
6 buried; LTC and DOGE are converted onto that same instant, and each consumer
is its own producer plus six hours at its own measured cadence rather than a
block count carried over from the first sizing.

Arms the TRAIN_ACTIVATION 0.20.0 row at testnet 153,116, 106 blocks and about
17 h below the BTC producer. resolveRuleSet reads only the local map, so with no
row every block above the boundary keeps resolving under 0.19.0, and a manifest
naming 0.20.0 halts a fleet in which no build implements it.

Records the cadence-window rule the re-cut used and makes the re-size rule per
chain instead of BTC-keyed, so an LTC or DOGE drift past the six-hour ordering
margin forces a re-cut the way an overrun BTC height already does.
Move balance helper test registrations into synchronous module-scope helpers while preserving suite order and hook scope. Reduce the two over-limit callbacks without adding await points or changing test coverage.
Extract the hash-continuity cases into bounded module-scope registration helpers. Preserve hook count, test order, titles, assertions, and synchronous scheduling.
Extract the config boundary groups into synchronous module-scope registration helpers. Preserve hook scope, title order, assertions, and zero-yield behavior while lowering the structural finding count.
… ISSUE landed in

A valid ISSUE re-derives the whole tokens row from the tick's issues history,
so an edit of an existing tick rewrites owner_id, description, the locks, the
callback and list fields and the bridge opt-in in place. Both action_index and
last_action_index stay pinned at the first issuance, so the action-scoped
stream carried the new issues row and never the tokens row it produced, and
the only in-place carry keyed on ticks touched by a credit, debit or escrow
row. An ownership transfer moves no balance, so every replica served the
pre-transfer owner until some later action on the tick happened to move one
(xchain-indexer#39: a TDOGE token kept its old owner for 24 blocks, then
flipped when an unrelated DESTROY landed).

Add the tokens-edit class beside the supply one, keyed on the ticks carrying a
valid issues row in the window. Forward only: the reorg direction needs a
replica-side re-derive and is registered separately.

The 400-line limit put the two tokens classes and the row accumulator into
named parts, and the integration proofs for the tokens table into one
directory. The registry note is the vendored half of the indexer's canonical
edit.
The armed LTC:testnet producer/consumer heights (4891504/4891766) are
already behind the live TLTC tip. Ruling dq4 (a), 2026-09-18: LTC:testnet
mirror admission ships null on this train and arms on a later train.
Sets both heights to null in this repo's shared_rows_2.js twin, with the
same replacement comment used across all six carriers.
Propagate the canonical v0.20.1 patch-train reslide into sync's own
copy of the shared consensus registry: BTC train height 153221, BTC
mirror producer/consumer 153300/153328 (producer also carries
ANCHOR_ATTEST_BARRIER_ACTIVATION), DOGE mirror producer/consumer
67916857/67917706. LTC:testnet stays null under dq4 (a) and is
untouched.

Re-pin bin/pins/identity.json (armed_map_fingerprint and the moved
train_activation.TRAIN_ACTIVATION row) to match.
Derive published fixture ports from the gate CI_PORT_OFFSET while preserving zero-offset defaults. Keep container ports fixed and route fixture clients through the same resolved host ports.
Re-vendor src/observability/ from xchain-hub via bin/sync-observability.sh:
the canonical module drops its underscore-prefixed method and handle names
and corrects stale comments. The test-only reset hook is now
resetObservability, so the suite that calls it follows the rename.
Move rebuild parity and client applier coverage into independent integration parts. Share the database hooks and fixtures without changing await counts or title order.
Partition polling, payload, probe, and status coverage into ordered parts. Share polling fixtures and hooks while preserving all nineteen test titles and await counts.
…rollback sweep

The twin of the indexer canonical: the note claimed no local pre-delete leg
exists while sweeps.js carries one inside the markers.
…ckpoint reads

/halt/clear and the raw server.on('upgrade', ...) guard in api.js ran in no
tier; the armed_source_handshake suite only exercised a hand-transcribed copy
of the upgrade check, which shared safeEqual but not the real handler's
shape. Added a bootRealApi() that proxyquires the real startApi() (network
edges stubbed) and captures the actual upgrade handler off the fake
http.Server, then drives both surfaces directly: /halt/clear's 401 with no
key configured, 401 on a wrong key, and 403 in server mode; the WS guard's
401 on no/wrong key and its pass-through to wss.handleUpgrade on a correct
one.

Also added direct unit coverage for the three state_checkpoints.js reads
(getLatestCheckpoint, findCheckpointsInRange, getCheckpointAtHeight), which
had none: each is asserted against its literal SQL shape, its bound params
(including the caller-supplied LIMIT), and an empty-result response.
… bound after bootstrap, fail closed on block-hash reads

The reconcile cadence knobs come from config, the wall-clock bound arms after
a full-snapshot bootstrap, BlockHasher fails closed on a read error, decoder
events leave content parity, pubkeys honor skip_lookups, the decoder scoping
sets derive from TOPOLOGY, and the snapshot-dispensers route docs match the
stream.
…d a cross-source markets id parity tool

A same-target duplicate-key failure that repeats now sets a visible halt with
its reason instead of retrying forever, and bin/markets-id-parity.js compares
markets ids by ticker across two sync sources. Stale lifecycle comments are
corrected.
…e-copy the table-lifecycle twin

reconcileDispensers now refuses a page whose schema_version does not match the
replica before collecting rows, so a version-mismatched follower no longer
replaces its dispensers table from the status tick.
… margin

Live testnet tips (TBTC 153698, TDOGE 67924122 at 2026-09-23T15:55Z)
have passed every height the 2026-09-19 reslide armed, so the freeze
preflight refuses them. The operator ruled a second reslide to 40 h
of margin to the nearest armed height at the fastest defensible
cadence (BTC least-squares bound 383.04 s/block, DOGE median 11
s/block), keeping the prior spacing.

Propagate it into sync's own copy of the shared consensus registry: BTC
train height 154074 (tip + 376 blocks, 40 h), BTC mirror
producer/consumer 154234/154291 (train + 160, 17 h; producer + 57, 6 h;
the consumer also carries ANCHOR_ATTEST_BARRIER_ACTIVATION), DOGE mirror
producer/consumer 67942777/67944741 (tip + 18655, 57 h, the same
instant as the BTC producer; producer + 1964, 6 h). LTC:testnet stays
null under dq4 (a) and is untouched.

Re-pin bin/pins/identity.json (armed_map_fingerprint and the moved
train_activation.TRAIN_ACTIVATION row) to match, regenerated with
bin/pin-identity.js --out.
Replace the opaque dq4 (a) decision label and an issue-tracker id
with self-contained public rationale. Comment text only; the
table-lifecycle twin stays byte-identical to xchain-indexer's
canonical copy, and the shared gate rows stay byte-identical to the
other five carriers.
…n reorg

Updated-rows class 7 carries an ISSUE edit's tokens row forward, but a reorg
that orphaned the edit left the replica on the edited row while the source
refolded it from the surviving issues (rollback/commit.js updateTokens). The
row's action_index is the first issuance, so the generic delete never touched
it and no later window re-sent it: a silent replica divergence on owner, the
seven locks, callback, lists, mint window and bridge policy.

ClientRollback now collects every tick the orphaned range issued on before the
delete and, after it and before the supply recompute, rewrites each tick's fold
columns from its surviving valid issues inside the rollback transaction. The
port (src/db/token_refold.js) restates getTokenInfo's replay and createToken's
normalize and fields step for step; a tick whose first issuance is not held
locally (truncated replica) is skipped and logged rather than folded from a tail.

The fold columns sat in no hash, so an advisory TOKEN_FOLD_PARITY_CHECK (default
off, never halts) now digests them on /status and at the follower's bootstrap
cross-check, bounded on the source by the published height's last action.

Tests run each reorg (ownership, lock, bridge policy, callback/list/mint window)
against the xchain-indexer's own fold when the sibling is present.
…iling mean

Operator ruling D11 (a) 2026-09-23: producer moves from 67942777 to
67936053 and consumer from 67944741 to 67936888, the same-instant rule
computed off the 84h trailing block-time mean. BTC:testnet heights are
unchanged; their comments now also carry the 84h trailing mean cadence
bound. Byte-identical to the other five shared-row carriers; sync's own
bin/pins/identity.json does not track this gate and is unchanged.
The floating node:22-bookworm tag can move to a Node patch whose V8/ICU
no longer match xchain-vm's consensus runtime pin, so validators built
from it would fail to join the fleet. Pinning by digest to a measured
match removes that risk.
@jdogresorg
jdogresorg merged commit d2f37e1 into master Sep 24, 2026
8 checks passed
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