Skip to content

Instantly share code, notes, and snippets.

@denniswon
Created July 14, 2026 23:53
Show Gist options
  • Select an option

  • Save denniswon/305a13e177c2797ab0597896c9cb9239 to your computer and use it in GitHub Desktop.

Select an option

Save denniswon/305a13e177c2797ab0597896c9cb9239 to your computer and use it in GitHub Desktop.
V2 protocol design challenge
※ recap: Goal: get the main→v2 sync PR #729 clean and root-cause the failing V2 eval/threshold CI suites. Both done: #729 verified mergeable, the failure traced to a known ordering race (NEWT-1884, re-opened). Next: awaiting your go-ahead on the defer-on-shortfall fix design.
❯ i dont understand how the race condition happens - gateway sequences 1 block for P1, P2 and P3 -> operators each get the "same" block , start from the same root for block execution state update, executes and return back to aggregator for bls aggregation right? then if quorum is met bootnode updates the state with the new root and state db, operators as well from gateway notifying that what the confirmed consensused root is, if this consensused root is the same for the operator, then it then updates it state db with the new updated root and flushing the state update to state db, if not the same, then operator reconciles through the DA layer (bootnode for now today). What am i missing here in terms of my understanding?
Great question — this is exactly the point where your model diverges from the code, and it's a single architectural assumption. Let me verify the precise contents of what the operator actually receives before I correct it, because the whole thing hinges on it:
Ran 1 shell command
Confirmed at source — and this pins the exact gap in your model. Here's what BlockFinalization actually carries (sequencer/src/wire/block_final.rs): the three roots, the membership ids (sequenced_p1_batch_ids, etc.), and block_delta_hash — a hash of the delta. It does not carry the batch contents or even the delta itself. And there's a single broadcast (newt_applyBlockFinalization, spawns.rs:207) — no second "here's the consensused root" message.
That's the one thing you're missing. Let me walk your steps.
"Operators each get the same block, start from the same root, execute." Half right. They start from the same prior root (both at genesis before the policy block — the earlier blocks are empty), but they do not receive the block's contents. They receive the finalization, which is a commitment (roots + ids + a delta hash), not the data. Each operator must assemble the block itself from its own local BatchBuffer — looking up the declared sequenced_p1_batch_ids — and re--runs the plane roots) to reproduce the roots. The buffer is fed by a separate, best-effort forward channel (broadcast_submit_batch), independent of the finalization. So "the same block" is the false step: both operators get the same finalization, bu the policy batch to operator A and not (yet) to B, thenA re-executes with the batch → policy root → matches the confirms; B re-executes without it → genesis root →mismatch. They diverge because they executed different b they executed the same block differently. Execution isdeterministic; the inputs weren't identical."Return to aggregator for BLS aggregation… if quorum is parate mechanisms get conflated. Block confirmation isnot a quorum vote and has no aggregator. Each operator independently verifies the gateway's broadcast root against its own recompute advances its own confirmed tip locally — no signatures, or quorum." The BLS aggregation you're describing is aseparate, later step that happens at eval time: when a cl, the gateway fans prepareEval (collect each operator'salready-confirmed tip) then commitEval (collect BLS part— over tips that block-confirmation already established.So the sequence isn't "execute → aggregate → quorum → upr re-executes → matches → updates its own state db(advances tip)," and then, independently, eval reads tho"Gateway notifies operators of the confirmed consensusednot, reconcile via DA." The reconcile-via-DA half isexactly right — that is the recovery path (reconcile_via "gateway notifies the consensused root" step, and the
"Operators each get the same block, start from the same . They start from the same prior root (both at genesisbefore the policy block — the earlier blocks are empty),he block's contents. They receive the finalization, whichis a commitment (roots + ids + a delta hash), not the daemble the block itself from its own local BatchBuffer —looking up the declared sequenced_p1_batch_ids — and re--runs the plane roots) to reproduce the roots. The bufferis fed by a separate, best-effort forward channel (broadndent of the finalization. So "the same block" is thefalse step: both operators get the same finalization, bu the policy batch to operator A and not (yet) to B, thenA re-executes with the batch → policy root → matches the gateway's declared root → confirms; B re-executes without it → genesis root → mismatch. They diverge because they executed different b they executed the same block differently. Execution isdeterministic; the inputs weren't identical.
"Return to aggregator for BLS aggregation… if quorum is met…" This is where two separate mechanisms get conflated. Block confirmation is not a quorum vote and has no aggregator. Each operator igateway's broadcast root against its own recompute andadvances its own confirmed tip locally — no signatures, or quorum." The BLS aggregation you're describing is aseparate, later step that happens at eval time: when a cl, the gateway fans prepareEval (collect each operator'salready-confirmed tip) then commitEval (collect BLS part— over tips that block-confirmation already established.So the sequence isn't "execute → aggregate → quorum → upr re-executes → matches → updates its own state db(advances tip)," and then, independently, eval reads tho
"Gateway notifies operators of the confirmed consensusednot, reconcile via DA." The reconcile-via-DA half isexactly right — that is the recovery path (reconcile_via_bootnode). But there's no "gateway notifies the consensused root" step, and the operator never adopts the gateway's root on faith. It onpendently reproduce that root from its own data. That's adeliberate trustlessness property: the operator won't siecompute — it's re-execution/fraud-proof-styleverification, not "receive block + adopt gateway's root.osed flip side of that property: when the operatorlegitimately lacks the data to reproduce the root (missi, so it refuses to advance and falls to reconcile — andreconcile needs the DA layer, which is unwired today, sonent.
So the corrected flow:
gateway: sequence block → commit its OWN tree → broadcast finalization (roots + batch IDs + delta HASH) │ │ (separate best-effort channel) zation) ▼operator BatchBuffer ←── raw batch ───┐ operator │ └──► operator: tabuffer buffer has batch? ─── yes CHES → confirm+flush, advance tip no ─FFERS → reconcile via DA (bootnode) │ dead → STALL forever
The load-bearing assumption the design makes — and the one that breaks — is data availability: operators can only re-execute if the batch reached their buffer, and that delivery is best-effort a finalization. Your mental model is actually the cleanerprotocol (gateway drives a root-consensus round and hands operators the block); the real one is leaner and shifts the burden onto per-operator re-execution, which makes batch data-availability the critical path. That's precisely why the two-part fix targets DA: make the forward reliable and let the operator wait for a decbatch before giving up.
Does that clear up the divergence? If so, I'll proceed with the defer-on-shortfall + forward-retry fix on a fresh branch.
✻ Churned for 2m 25s
※ recap: Goal: land the main→v2 sync (PR #729, clean and mergeable). Now investigating a pre-existing eval-quorum CI failure (NEWT-1884, re-opened) caused by a batch-forward race. Next: on yourefer-on-shortfall plus forward-retry fix on a freshbranch. no do not proceed, and let's design the actual correct dsion -> block construction via gateway sequencer ->operator gets the ordered block (p1: ordered, p2: not ordered, p3: not ordered) -> operator executes p1, p2, p3 -> return the updated state root (not finalized yet) -> gateway aggregates andorum met, return the result to users, notify bootnode(posting to DA layer) and operators to now flush the staer, if an operator finds the consensus root different(posting to DA layer) and operators to now flush the staer, if an operator finds the consensus root differentthan its own root that one submitted to the gateway, the need to reconcile from DA layer directly (bootnode today) to get the correct state root and state delta to update its state db. Gatewor any persistent layer, it purely orchestrates, onlybootnode (DA layer) and operators do (in alignment with future direction of gateway being deprecated with p2p layer + shared mempool) /clarify-planUnderstood — not proceeding with the patch. You're descrt architecture (a quorum-gated block finalization whereoperators propose roots and the gateway holds no state),one. Let me load the clarify-plan workflow and drive thisproperly.
Skill(/clarify-plan)Successfully loaded skillI have the current implementation fully traced, so I canific to the exact forks between your intended design andwhat's built today. Your design decides the big things (gated finalization, DA-reconcile, p2p/shared-mempoolfuture) — these questions resolve the under-specified parts before I write anything.
One framing note that shapes everything: in your target,ven exist. Today operators re-derive the block from a
separately forwarded buffer (the race). In a shared-mempalready holds the batch contents, and the sequencer
distributes only the order — so there's nothing to race.rom patching the forward to building toward the
shared-mempool ordering model.
⏺ User answered Claude's questions:
· What should this design session produce? → Target A
· In the target, how do operators obtain the P1 batch CONTENTS (not just the order)? → 1, performance and scalability + reliability is
the key, so suggest the best approach
· How does the new per-block root-quorum relate to the existing eval-time BLS quorum? → 1 if it is possible, since after the quorum/bls
aggregation, the gateway should immediately return thnse back to the client. if not possible, then 2
· In the stateless-gateway model, who commits the deluorum, and is wiring DA a hard prerequisite? → 3, but
bootnode can also have access to shared mempool if thncy, scalability, Again, evaluate and propose the best
approach
Good — you've delegated the technical calls, so I'll proeach and converge to a target design, then confirm before writing the ADR. Here's where I land, with the priorities you named (performance · scalability · reliability, and the project's
correctness > latency > trustlessness).
Proposed target flow
client intent ──► SHARED MEMPOOL (content-addressed batches; operators + bootnode read/write)
sequencer (gateway today → p2p leader later): pure or
│ broadcasts ORDER ONLY: block3 member sets} — a proposal, no root
each operator: fetch contents BY ID from mempool (alr→P3 from current tip
│ → compute (control_root, datITTED
│ → return proposed root + BLSot)
gateway AGGREGATES: tally roots, ≥66% stake on the SAteway root exists.
quorum met ─┼─► return the aggregated cert to the client IMMEDIATELY (this cert IS the eval attestation)
├─► signal operators to FLUSH b
└─► relay the block HEADER (order + root + cert) to DA (bootnode)
minority/diverged operator: detected at aggregation → reconcile from DA (canonical delta+root) → catch up
The delegated decisions, resolved
- Block distribution → shared-mempool, order-only (your t for all three priorities and it structurally deletes
the current bug: contents are content-addressed in the mnever "missing" a batch from a raced per-operator forward — it fetches by id (from mempool, or pulls from DA/peers if unseen). The sequencer ships only the order. No best-effort forward exists to
drop or reorder.
- Quorum → unify (your option 1), and it's both feasible and faster under load. The eval is a P2 op inside a block; the block's root-quorum
certifies it, so the aggregated BLS cert is the attestat — no separate submit_eval round. Feasibility: thecurrent eval path already does a per-eval prepare/commit BLS round; unifying moves that to one round per block, amortized over every eval
in the block. Under load that's fewer rounds (throughputcy floor of ~one tick for a lone eval (tunable via
tick_ms). Net: better scalability, same crypto machinery
- DA → gateway relays a small header, holds nothing; bootnode reads the shared mempool for contents (your option 3 + your bootnode-mempool
idea). The gateway posts only {order, canonical root, qur, no state, keeping it a pure orchestrator. The contents
are reconstructable from mempool + order, and since the ared mempool, DA needn't store batch bodies at all — it
stores headers and serves reconcile by pointing at mempoootprint, best latency/scalability. Wiring a functionalDA/bootnode becomes a hard prerequisite — it's the reconcile backstop, and its absence today is exactly why divergence dead-ends.
Properties this buys (vs today)
- Race gone by construction — no per-operator content forward; deterministic same-order-same-contents-same-root.
- Stateless gateway — no tree, no unilateral root; the c the operator quorum. Directly on the p2p/shared-mempool
path (sequencer → leader election; aggregation → gossip)
- Trustless + self-healing — divergence is detected at a, not silently stalled; the minority reconciles from DA
to the quorum root, never trusts a gateway root (there i
- Reliable — no single dropped message halts anything; a a detected minority that catches up; a real persistentminority is a fraud signal for the slashers (roadmap SP-6).
The one hard tradeoff — latency
Block finalization becomes a consensus round-trip (operator execute + BLS aggregate) instead of the gateway committing instantly. Honest
accounting: the sub-ms requirement is for reads (signed untouched. A quorum'd write/eval is inherently a
consensus round in any trustless design; unify makes it zed rather than one-per-eval, so under load it's a
throughput win with a ~tick latency floor for a lone eva
2PC failure semantics (my proposal)
Operator holds the delta uncommitted between propose and no-flush / timeout: discard the uncommitted delta andre-sync the head from DA. No data loss — contents live in the mempool/DA and re-execution is deterministic and cheap.
Deliverable + phasing Per your choice (ADR + phased tickets): I'll write an ADpose into sequenced tickets, roughly — (1) operatorssource batch contents by id from a shared source insteadd [kills the race, forward-compatible]; (2) statelesssequencer: move root computation off the gateway to oper(3) DA/bootnode as first-class prerequisite + reconcile;(4) unify block-quorum with the eval cert; (5) p2p ordergateway deprecation). The current CI reds get a separateminimal-unblock ticket aligned with phase 1 (operators prtfall rather than stall) so #729 and the suite aren'theld hostage to the full re-architecture.Note (lessons.md): phase 2/4 touch the BLS reference-bloblock must match the block the signature was builtagainst") and the operator-set-snapshot discipline — thet-quorum signs over a fixed reference block, same as theeval/state-commit paths today.Before I write the ADR and file the tickets: does this tespecially unify-quorum (one cert per block, returnedimmediately) and stateless-gateway-relays-header-only? Atick/latency floor as a tunable, or do you have a targetnumber (e.g. sub-second block finality)?
✻ Sautéed for 5m 17s
❯ "design the actual correct design of the intent submissiia gateway sequencer -> operator gets the ordered block(p1: ordered, p2: not ordered, p3: not ordered) -> opera return the updated state root (not finalized yet) ->
❯ "design the actual correct design of the intent submission -> block construction via gateway sequencer -> operator gets the ordered bloc(p1: ordered, p2: not ordered, p3: not ordered) -> opera return the updated state root (not finalized yet) ->gateway aggregates and check for quorum -> if quorum metrs, notify bootnode (posting to DA layer) and operatorsto now flush the state delta to persistent layer, if an us root different than its own root that one submittedto the gateway, the need to reconcile from DA layer direet the correct state root and state delta to update itsstate db. Gateway does NOT hold state db or any persistetrates, only bootnode (DA layer) and operators do (inalignment with future direction of gateway being deprecad mempool)" --> with this original intent in mind,
/codex:adversarial-review before proceeding
Right call — stress-test the design adversarially beforeI'll run Codex (your explicit ask) plus an opusarchitecture-critic lane in parallel, both anchored on your original intent, both grounded against the current code so the critique is concrete rather than abstract. Launching them now.
Called validate-plans-and-brainstorm-ideas
⏺ oh-my-claudecode:architect(Adversarial architecture crit
Backgrounded agent (↓ to manage · ctrl+o to expand)
Codex's MCP tool idled out at 30 minutes and aborted — no result. The architect (opus) lane is still running. The user specifically wants
the Codex adversarial pass, so let me get it via a path stead of the silent MCP call. Checking for the Codex CLI:
Ran 1 shell command
Codex CLI is available (0.144.1) — I'll drive it via codex exec under nohup with a hard timeout and output capture, which the silent MCP call didn't allow. Writing the prompt and wrapper:
Made 2 scratchpad edits +49, ran 1 shell command
Codex is running. Let me confirm the wrapper (which writes the completion marker) is alive too — if setsid misbehaved, the marker path could break — then arm a monitor:
Ran 1 shell command
Three codex processes and my wrapper isn't showing in pgrep — that's ambiguous (one is likely the aborted MCP codex still aserver-side). Let me not rabbit-hole on PIDs; the decisiview's output file is growing and whether themarker-writer survived:
Ran 1 shell command
That was the macOS-no-setsid trap (my own notes warned about it) plus a pgrep false-positive — my wrapper died instantly and never launched codex; the three codex processes are hours-old ambient ones, not mine. Relaunching with plain nohup and this time assertingoutput file, not pgrep:
Ran 1 shell command
Codex is genuinely running now (log materializing at 8.3K and growing). Arming a monitor for its completion marker while thruns in parallel:
⏺ Monitor(codex design-review completion or stall)
Monitor started · task b8x4hijxf · timeout 1800s
Both adversarial lanes are now running — Codex (bounded, output-captured, monitored) and the opus architect critique. I'll into one verdict when they land, then adjust the design thing to do until they report.
To be clear on where we are: no ADR written, no code touched, not proceeding — exactly as you asked. The design is being stress-tested first. When the two lanes return, I'll give you the consolidated adversarial findings (ranked, Band A first) against your intent — deterministic-execution risk, the stateless-gateway/order-equivocation question, DA-withholding, unify-quorum coupling, and whatever else they surface — and only then propose the refined design for your sign-off. Stop says: [ContinuousLearning] 455 user messages thiextract reusable generic patterns into/Users/denniswon/.claude/skills/learned/ (project-specific lessons go to the repo's incidents inbox instead)
✻ Crunched for 36m 5s
⏺ Agent "Adversarial architecture critique of V2 quorum design" finished · 6m 52s
The architect lane returned a serious, docs-grounded critique that essentially says the proposed redesign is a net regression against the existing V2 consensus model — and it cites design docs I hadn't consulted when I elaborated the design. Before I relay a crcontradicts a direction you proposed, I must verify its rce. Checking the four Band-A anchors while Codexfinishes:
Ran 1 shell command
Verified at source — the architect's Band-A findings holhis is a significant course-correction I need to put infront of you now rather than after Codex finishes, because it contradicts the direction we were heading and part of it is m
The headline: the adversarial review found the proposed redesign is a net regression against the existing V2 consensus modeexisting model already deliberately made the opposite chhadn't consulted. I proposed the "unify-quorum"elaboration without reading the V2 design-of-record (CYC.md), and that was a mistake. Here's what's verified:F1 (Band A) — quorum-decides replaces a fail-safe veto wst, and V2 has no slashing. Today the per-block root is adeterministic function of (ordered inputs, prior state);omputes a different root halts loud — no majority cansilently overrule it (ARCHITECTURE.md:16 "failures loud,on-reconcilable halt). Flip to "operators propose →quorum decides" and a byzantine/buggy ≥threshold set finalizes a wrong root with the honest minority outvoted. ROADMAP.md:5verbatim: "Nothing in V2 can slash anyone" — the slasherallenger has zero V2 refs. A quorum over a deterministicfunction adds nothing but a liveness round unless paired with fraud-proofs + slashing to make the majority accountable. This directly hits priority #1 (correctness).
F2 (Band A) — unify-quorum detonates the two-tier amortitency to whole-block finality. CYCLE_ANCHOR.md table,verified: per-block is "verify-by-recompute (no per-blocve BLS is deliberately amortized to the per-cyclestate-commit (60/30/2 blocks), "off the operator criticaLS-round-per-block reintroduces exactly what V2 removed.And folding the eval cert into block finality violates Ais no cross-pipeline atomicity" — a P1 conflict or P3divergence would hold an unrelated P2 eval cert hostage.F3 (Band A) — "stateless gateway" is a category error. Tcritical state — the order is (it's bound intoblock_hash). Removing the gateway's local tree doesn't rgateway can still equivocate on the order to differentoperator subsets, and the design adds no signed-order-tong to detect it.F4 (Band A) — DA/bootnode becomes a synchronous single-c, recovery, and content persistence — amplifying theexact terminal-Stall the redesign aims to remove (singlehot-standby; BOOTNODE_SIDECAR.md:13).The fair synthesis: your direction is right and is alrears hold state, the gateway is never a correctnessauthority, the end state is p2p + shared mempool. The dind mechanism: the existing V2 reaches trustlessness via(a) per-block verify-by-recompute veto, (b) per-cycle amaud-proofs + slashing (SP-6, not yet built), (d) DA-HA +p2p (later). The proposed redesign ships a per-block quohing + DA-HA + p2p machinery that would make it safe,which inverts correctness > latency > trustlessness now to buy a trustlessness it doesn't actually complete (the gateway stays central as sequencer and aggregator). And the tip-alignment bug that started this — that's a separate, smaller batch-availability racethe current model, not requiring the redesign.
I'm not proceeding to an ADR. Codex is still running (yolands I'll fold it in and give you the consolidatedverdict plus the corrected recommendation — most likely "keep the verify-by-recompute model, fix the batch-availability race incrementally, and invest in the already-designed cycle-anchor + SP-6 slashing + DA-HA path toward the same trustless end state." But I wanted you to have the verified headline now, including that my unify-quorum steer was wrong. Worked for 40m 48sMonitor event: "codex design-review completion or stall"Codex finished (exit 0). Extracting its actual findings tly hook/reasoning noise — I need the final answer):
Ran 1 shell command
Monitor "codex design-review completion or stall" stream
Both lanes are in and they independently converge — the architect (verified at source against the V2 docs) and Codex (13 Bagrounded in specific code). That convergence is strong sated verdict.Verdict: Reconsider approachBoth adversarial passes reach the same conclusion: the pedesign is a net regression against the existing V2model, because it ships a per-block quorum vote ahead of the machinery (fraud-proofs + slashing, DA high-availability, sequview-change, p2p) that would make a vote safe. Your direte, gateway is never a correctness authority, end-stateis p2p + shared mempool — is right, and it's already the V2 target. The disagreement is mechanism and sequencing.
The Band-A blockers (both lanes, deduped, grounded)
1. Veto → vote, with no slashing to make the majority aclock root is deterministic and any honest operator thatrecomputes a different root halts loud — no majority canHITECTURE.md:16, verified). A quorum vote lets abyzantine/buggy ≥threshold set finalize a wrong root ove ROADMAP.md:57 (verified verbatim) says "Nothing in V2can slash anyone." A quorum over a deterministic functioess round until SP-6 slashing exists.2. Unify detonates the deliberate two-tier amortization ent reference blocks. CYCLE_ANCHOR.md (verified) makesper-block explicitly "verify-by-recompute (no per-block BLS round)" and amortizes the expensive BLS to per-cycle, off the cCodex adds the sharp crypto reason unify is unsafe: an eset reference block, the state pins a height, and theeval pins a reference_time_block — a block-root cert doulates all three and becomes unverifiable/slash-unsafe(submit_eval.rs:1196,1490).3. "Stateless gateway" is illusory — and the header can'order is the consensus-critical state, not the DB; asingle gateway can still equivocate the order to differected (both lanes). Codex adds the killer detail for my DAresolution: block_hash does not bind block_delta_hash, aes the JMT version operators need to replay(digest.rs:32,68) — so a header relayed to DA cannot prove or reconstruct the transition. My "gateway relays header, contenreconstructable" idea is broken as specified.
4. DA becomes a synchronous single-copy SPOF on the write path, recovery, and content persistence — amplifying the exact teredesign aims to remove (single bootnode/key, not in the:143).5. Determinism hazards inside your own flow (Codex's sha attacks the design's premise directly): "p2/p3 notordered" is a divergence trap — conflicting duplicate wrresolved by insertion encounter order(tree.rs:1254,1271), so two honest operators applying thfferent orders compute different roots. And tick_ts islabeled "advisory" but is in the block_hash preimage (veif operators sign headers, local wall-clock makes honestsignatures diverge. A propose-and-sign scheme needs bothrators split.6. Minority >34% flips from retryable to chain-halt. Tods retryable at eval (eval_quorum.rs:164); underblock-root quorum with no view-change, a minority can blut finalizing anything false — a new liveness hole.What both lanes agree is right (keep these)Operators-hold-state / gateway-holds-none is the correctsed order-only distribution is cheap and half-builtalready (block_final ships ids, not contents). P1 sender/nonce ordering is sound and must be preserved.
Recommendation (both lanes converge)
Don't build the propose→quorum redesign now. Instead: - Keep the per-block verify-by-recompute + halt model — est operator a veto and already amortizes BLS toper-cycle.
- Fix the tip-alignment bug incrementally within it — that's the actual thing that started this (the batch-availability racand it's a small fix, not a re-architecture.
- Reach trustlessness via the path V2 already designed: the cycle-anchor BLS (exists) + SP-6 fraud-proofs/slashing (the reathat makes any quorum accountable) + DA-HA + p2p/shared-hat also replaces gateway ordering. Same end state,without inverting correctness > latency > trustlessness en route.
I owe you a straight correction: my earlier "unify-quorum" steer was made without reading the V2 design-of-record, and it wfights the two-tier model that is the core of V2.Given both lanes reject the redesign, I don't think an An. How do you want to proceed? My recommendation: (a)land the incremental NEWT-1884 fix within the current mo, and (b) treat "trustless, off-gateway" as the SP-6 +p2p roadmap work it already is — and if you want, I'll wrd capturing why the propose→quorum redesign wasconsidered and rejected (these findings), so it's not re Codex + architect findings are saved if you want themattached.
✻ Baked for 1m 54s
recap: Goal: fix the V2 eval/threshold CI failures and d design. Adversarial review rejected the proposed quorumredesign, favoring an incremental fix on the current model. Next: your call on proceeding with the NEWT-1884 fix versus the redesign.
sounds good. yes attach the codex + architect findings. honest operators applying the same unordered set indifferent orders compute different roots." --> for p2 and p3, those are policy evaluation tasks only for p2 and data ingestion tasks only for p3, these don't need to be ordered right? because the output leaf will never overlap, so arent these tasks, pipelines r
That's a sharp challenge to Codex's F9, and you may well be right — a Merkle root is a function of the set of key→value writes, so if P2/P3 outputs never collide on a leaf key, application order can't change the root by construction. Codex's finding is conditional ("if unordered members contain conflicting writes for the same leaf"). So the real question is empirical: can two P2 (or two P3)block ever write the same leaf key? Let me verify at souher of us:
Ran 1 shell command Verified — and you're right; Codex's F9 is a misread. Thly. Here's the evidence:- P2 output keys are unique by construction: task_responcy_client ‖ addressing_input ‖ nonce_le)(leaf_key.rs:169), and P2 members are dedup'd by subjectncer.rs:504) — and subject_id is derived from those sameinputs (leaf_key.rs:164), so dedup-by-subject_id is dedut P2 members can't write the same leaf.- P3 output keys are unique too: policydata = 0x02 ‖ kece) (leaf_key.rs:131), and members are dedup'd byleaf-key-hash at the sequencer (sequencer.rs:506).- The three pipelines write disjoint namespaces (P1 contx02), so no cross-pipeline collision either.- The JMT makes order irrelevant for a unique-key set — verbatim at the exact function Codex cited(classify_batch_monotonicity, tree.rs:1254): "Byzantine-resistance invariant: the verdict depends only on the SET of (key, version, hciphertext) entries, never on their order… every operatoreject decision regardless of how the batch wasassembled or replayed." The "Pass 1 (insertion order)" Codex latched onto only decides which duplicate gets reported as the offender cosmetic, it doesn't change the accept/reject decision o
So your model is correct: P2/P3 outputs never overlap, un a Merkle tree, and honest operators reach the same rootregardless of application order. F9 is refuted — its antes for the same leaf") is prevented by the dedup +content-addressed keys, and its mechanism claim (order-sadicted by the tree's own documented invariant. Good
catch; this is exactly why we verify a reviewer's claimsk them — even a strong adversarial pass throws falsepositives.
Two caveats so I'm precise: it doesn't change the overall verdict — F1–F4 (veto→vote without slashing, unify detonating the two-tier
amortization + conflating reference blocks, illusory stae all verified and stand on their own. And Codex's F10(tick_ts sits in the block_hash preimage yet is labeled "advisory") is a separate header-determinism point that only bites if operators sign the header — i.e., only in the rejected propose-and-sign redesign; it's moot for the current verify-by-recompute model.
Now attaching the findings. Writing the decision record refuted with this evidence):
Made 1 scratchpad edit +44
Making it durable — creating a decision-record ticket in the V2 project with both lanes' findings, linked to NEWT-1884:
Called linear-server
Done. Decision record filed as NEWT-1955 (V2 project, reoth lanes' findings, the source-grounded Band-A verdict,
and the F9 refutation captured — so this won't get re-li
Where this leaves things:
- Your P2/P3 question: you were right, verified at sourcjoint namespaces with unique content-addressed keys
(dedup'd by subject_id / leaf-key-hash), so the JMT rootonstruction — the tree code even documents the invariant
verbatim. Codex's F9 was a misread and is marked refutedall verdict: F1–F4 stand on their own.
- The design verdict stands: don't build the propose→quoection (operators hold state, gateway never a correctness
authority, p2p end-state) is already the V2 target; the a per-block quorum vote ahead of the slashing + DA-HA + view-change + p2p that would make it safe, regressing correctness/latency/reliability today. The path V2 already designed —
verify-by-recompute veto + per-cycle amortized BLS + SP- reaches the same end state without inverting thepriority order.
- The actual CI failure remains a small, separate fix: Nbility race, fixable within the current model — that'sthe concrete next step to green the eval/threshold suites whenever you want it done.
- PR #729 (the main→v2 sync) is untouched by all of this — the E2E reds on it are these same pre-existing suite
failures, not sync regressions.
Nothing is committed to code from this design exploratio a decision record and a grounded verdict. Say the word
if you want me to take on the NEWT-1884 incremental fix,chitect/Codex outputs to NEWT-1955.
✻ Crunched for 4m 48s
※ recap: Goal was landing the main→v2 sync PR #729, which then investigated the V2 eval CI failures and filed
decision record NEWT-1955 rejecting a consensus redesignn the NEWT-1884 incremental fix.
❯ so at which level does bls quorum aggregation happen for block level (ever 1 second) p2: every task level, p3:
every task level (with median consensus)? and the for the? (60 seconds? 120 seconds?)
Good precise question — let me verify the exact cadencesonstruct from memory, since this is the kind of thingthat's easy to get subtly wrong. Reading the authoritative cadence doc and the per-pipeline aggregation code:
Ran 1 shell command
Cadences confirmed for the block-root and state-commit surfaces. Let me nail the remaining ambiguity — whether P1 and P3 each have their
own BLS attestation (the table says "per batch (P1)") antually is (modal vs median):
Read 1 file, ran 2 shell commands
Now I have the pieces. One more check to be certain P3 ias no per-task BLS of its own, then I'll answerprecisely:
Ran 1 shell command
All verified. Here's the precise picture — and it untangles two things your hypothesis merges.
The key distinction: "BLS quorum" and "block-root consens. The per-block root (~1s) is not a BLS round — it'sverify-by-recompute. The BLS quorums are the per-task/per-batch attestations (P1, P2) and the per-cycle state commit. P3 doesn't use BLS at
all.
┌───────────────────────────┬────────────────────┬───────────────────────────────────────────┬──────────────┐
│ Surface │ Cadence │ Mechanism │ BLS? │
├───────────────────────────┼────────────────────┼───────────────────────────────────────────┼──────────────┤
│ P1 (PutLeaf batches) │ per batch │ BLS quorum over TaskResult{subject_id = batch_id} │ ✅ │
├───────────────────────────┼────────────────────┼───────────────────────────────────────────┼──────────────┤
│ P2 (policy eval) │ per task │ BLS quorum over TaskResult{subject_id = task_id} — exact-digest │ ✅ │
│ │ │ agree │ │
├───────────────────────────┼────────────────────┼───────────────────────────────────────────┼──────────────┤
│ P3 (PolicyData ingestion) │ per ingestion tick │ modalodalAggregator) → commits as a 0x02 │ ❌ │
│ │ │ leaf │ │
├───────────────────────────┼────────────────────┼───────────────────────────────────────────┼──────────────┤
│ Block root (all 3 │ per block (~1s) │ verify-by-recompute │ ❌ │
│ combined) │ │ │ │
├───────────────────────────┼────────────────────┼───────────────────────────────────────────┼──────────────┤
│ State commit │ per cycle (K │ BLS qteRoot │ ✅ │
│ │ blocks) │ │ (on-chain) │
└───────────────────────────┴────────────────────┴───────────────────────────────────────────┴──────────────┘
Three corrections to your model:
- P1 is per-batch, not "block level." Each batch produces a TaskResult (subject_id = batch_id, per task_result.rs:33) that operators BLS-sign. The ~1s block cadence is the verify-by-recompuno BLS. So P1's BLS attestation is per-batch; the blockroot is a separate, no-BLS surface. - P2 per-task is right ✅, but it's exact-digest agreemem.rs = "stake-weighted BLS quorum over P2 evaluation results." The TaskResult digest binds anchor_root, refer_ts, and emits, so a quorum forms only across operatorsthat evaluated the same committed state at the same reference and produced identical results. No median smoothing — operators must match. - P3 is modal, not median — and not BLS. Per aggregator. Data WASM and produces an IngestionResult; the ModalAggregator converges cross-operator at the byte levcommon per the feed's ThresholdConfig), and the converged value "commits as a 0x02 PolicyData PutLeaf in the next because operators fetching slightly different externalHTTP data need a most-common tiebreak, not an average. P3 gets its consensus coverage from the block-root recompute + the per-cycle state commit — there's no standalone P3 BLS cert. (Median-baseregator model for numeric task responses; v2 usesexact-digest BLS for P1/P2 and modal for P3.)
State commit cadence: per cycle, triggered by the cycle-anchor watcher at each height % cycle_blocks == 0 boundary — K=60 on mainnet ≈ 60 (at ~1s/block), K=2 for local/test. The 120s figure is a it's now per-cycle-watcher-triggered, and the docs say "the 60s cycle governs." This is the only surface that rteRoot), amortizing gas across the cycle — the on-chain submission body itself is still being wired, but the tri So: P1 per-batch BLS · P2 per-task BLS · P3 per-tick moder-second recompute (no BLS) · state-commit per-cycle (~60s) BLS + on-chain.
Cooked for 3m 44s
recap: Main→v2 sync PR #729 is verified clean and ready pre-existing eval/threshold suite failures, not regressions. Next: land the NEWT-1884 batch-availabilitydel to green those suites, whenever you want it done. 1. "- P3 is modal, not median — and not BLS. Per aggrega the Data WASM and produces an IngestionResult; the ModalAggregator converges cross-operator at the byte levcommon per the feed's ThresholdConfig), and the converged value "commits as a 0x02 PolicyData PutLeaf inot median) because operators fetching slightly different external HTTP data need a most-common tiebreak, not an average. " --> dont we do median consensus? are you sure we do modal? why was this changed for v2? 2. for P2, for any policy that involves do threshold decryption right? (not today, but once we deprecate shared hpke decryption and fully migrate to threshold decryption), those p2 tasks require threshold decryption meaning it
requires decrypted partials to be aggregated and then meE by each operator. Same for p3 tasks that involvessecrets and need threshold decryption for executing wasms with secrets. 3. For P1, by do we need per-batch bls? also is a batch same thing
as a block? 4. the converged value "commits as a 0x02 Poxt block." --> how does this exactly happen?
These deserve source-verified answers — you've already ce, so let me not guess on any of them. Gathering evidence
for all four in parallel:
Ran 1 shell command
This already corrects my P3 answer — the user is right. Let me finish verifying P1 (which I may have overclaimed), the P3→PutLeaf
mechanism, and threshold, then give a fully corrected an
Ran 1 shell command
Two corrections landing: P1 has no wired per-batch BLS (nd median very much exists. Let me nail the P3→block
mechanism (Q4) and confirm the threshold-decryption flowly corrected answer:
Ran 1 shell command
All four verified — and I owe you two corrections to my ht to push on the median point, and I over-claimed on P1.
Here's the accurate picture with sources.
Correction 1 — median (Q1): you're right, median is realrent baseline
Median didn't go away for v2 — it exists in three places, and I collapsed them:
- v1 aggregator uses median-based consensus today: aggregator/core.rs:94,1911 — "median-based consensus with configurable tolerance" over
task-response digests.
- v2 P3 ingestion design keeps median as a feed-declared option: AggregationFn::{Median, Mean, First} (feed.rs:142, threshold.rs:14) for
volatile numeric output fields.
- But the current v2 M2 implementation ships byte-level 9-31): per-field numeric median/mean "requires the forked
Regorus SchemaValidator and lands in a follow-up milestogregator applies byte-level modal convergence regardless
of the declared AggregationFn," and a feed whose numericge surfaces as Diverged rather than being
median-reconciled.
So "why for v2": it's not median→modal. v2 separates it igest agreement (operators evaluate at a gateway-pinnedanchor, so honest operators produce identical results; v1's median-with-tolerance existed precisely because operators evaluated at slightly
different states). For P3, external data still varies aceak is needed there — and modal (byte-level most-common)
is the M2 baseline, with per-field median deferred until"modal not median" was only describing the M2 baseline;
the design keeps median for P3 numeric fields.
Correction 2 — P1 has no wired per-batch BLS (Q3a)
I said "P1: per-batch BLS ✅" — that's wrong. Verified: -weighted BLS quorum over P2 evaluation results" (P2
only), and submit_batch.rs has zero BLS/partial/quorum rtype can carry subject_id = batch_id (a design provision
in the wire type), but no live path builds or signs a P1your question "why do we need per-batch BLS?" — we don't,
and we don't have it. P1 writes are deterministic state comes entirely from the block-root verify-by-recompute
(~1s) + the per-cycle state commit. The only live per-ta
Corrected table — the only BLS quorums are P2 (per-task)ommit:
┌────────────────┬────────────────────────────────────────────────────────────────┐
│ Pipeline │ Per-task/batch consensus │ BLS? │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ P1 (batches) │ block-root recompute + per-cycle statno per-batch attestation) │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ P2 (eval) │ per-task exact-digest BLS quorum (eva │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ P3 (ingestion) │ per-tick modal convergence → canonicano per-tick BLS) │
├────────────────┼──────────────────────────────────────────────────────────┼───────────────────────────────┤
│ block root │ per ~1s, verify-by-recompute │
├────────────────┼──────────────────────────────────────────────────────────┼───────────────────────────────┤
│ state commit │ per cycle (K blocks; ~60s mainnet) + │
└────────────────┴────────────────────────────────────────────────────────────────┘
Q3b — batch ≠ block
Distinct. A Batch (batch.rs:20) is one client submissionc<PutLeaf> } — one signer, one nonce, a set of writes,
content-addressed as batch_id. A block is one sequencer the P1 batches admitted that tick (canonical (sender,
nonce) order) plus the P2 emits and P3 outputs that finaalization carries sequenced_p1_batch_ids (plural),
p2_member_subject_ids, p3_member_output_ids. So many bat the client-submission unit, a block is the
Corrected table — the only BLS quorums are P2 (per-task)ommit:
┌────────────────┬────────────────────────────────────────────────────────────────┐
│ Pipeline │ Per-task/batch consen BLS? │
├────────────────┼──────────────────────────────────────────────────────────┼───────────────────────────────┤
│ P1 (batches) │ block-root recompute + per-cycle statno per-batch attestation) │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ P2 (eval) │ per-task exact-digest BLS quorum (eva │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ P3 (ingestion) │ per-tick modal convergence → canonicano per-tick BLS) │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ block root │ per ~1s, verify-by-recompute │
├────────────────┼────────────────────────────────────────────────────────────────┤
│ state commit │ per cycle (K blocks; ~60s mainnet) + │
└────────────────┴────────────────────────────────────────────────────────────────┘
Q3b — batch ≠ block
Distinct. A Batch (batch.rs:20) is one client submissionc<PutLeaf> } — one signer, one nonce, a set of writes,
content-addressed as batch_id. A block is one sequencer the P1 batches admitted that tick (canonical (sender,
nonce) order) plus the P2 emits and P3 outputs that finaalization carries sequenced_p1_batch_ids (plural),
p2_member_subject_ids, p3_member_output_ids. So many bat the client-submission unit, a block is the
sequencing/consensus unit.
Q2 — threshold decryption for P2 private-data and P3 secghtVerified in the docs. ARCHITECTURE.md:463: decryption ofhappens only inside operator TEEs — "Phase 1Ashared-HPKE-key already shipped; Phase 1B+ threshold MPKcombine inside TEE." ARCHITECTURE.md:127: P3 = "WASM +threshold-decrypted secrets, in TEE." IDENTITY_DATA_LIFEmode uses the DKG MPK with "t-of-n partial-DH combination
Verified in the docs. ARCHITECTURE.md:463: decryption of any enc_scheme=0x01 leaf happens only inside operator TEEs — "Phase 1A
shared-HPKE-key already shipped; Phase 1B+ threshold MPKcombine inside TEE." ARCHITECTURE.md:127: P3 = "WASM +threshold-decrypted secrets, in TEE." IDENTITY_DATA_LIFECYCLE.md:61-63: threshold mode uses the DKG MPK with "t-of-n partial-DH combination
(no single key opens it)," and "enabling threshold mode t (identity, ephemeral, private feeds) MPK-threshold at
once." So exactly as you describe: once shared-HPKE is drivate data (0x01 identity/confidential) and P3
secrets-bearing WASM both require operators to produce pine inside each operator's TEE to decrypt before
execution.
Worth flagging one consensus implication of this: threshtors are not fully independent for those tasks — each
must obtain a t-of-n set of partial-DH shares (a cross-oan execute the eval/WASM. That inter-operator dependency
is a real factor any "operators independently execute → account for, and it's another reason the current design
keeps the expensive coordination amortized rather than p
Q4 — how the converged P3 value becomes a 0x02 PutLeaf i
Traced through crates/gateway/src/ingestion/mod.rs + cra.rs + handler/mod.rs:470-474:
1. Each operator, per IngestionTick, runs the Data WASM ult, signs it, and POSTs a SubmitIngestionResultRequestto the gateway's POST /ingestion-result. 2. The gateway recovers + verifies the operator's signatner) against the claimed operator, then feeds the result into the ModalAggregator keyed by (feed_id, tick_id). 3. On AggregationOutcome::Converged — the one-shot commimon) output is held by ≥ min_sources operators per the feed's ThresholdConfig — the handler evicts the tick andlt as a 0x02 PolicyData PutLeaf into that chain's P3 lane (the sequencer's P3 input).
4. The sequencer includes that 0x02 leaf in the next blober_output_ids).
5. Operators receive the BlockFinalization, apply the 0xion, and verify-by-recompute confirms the block — the
converged value is deterministic (the modal winner), so the identical 0x02 write.
So the gateway is the P3 modal-aggregator and the admittData leaf into the sequencer lane; the actual So the gateway is the P3 modal-aggregator and the admittData leaf into the sequencer lane; the actualon-chain/consensus coverage of that leaf then comes from the block-root recompute + per-cycle state commit — not a P3-specific BLS cert.
Net corrected model: P2 is the only per-task BLS quorum; P1 and P3 have none (block-root + cycle-commit covers them); P3 converges by modal today (median/mean per-field deferred); state commit is ain BLS; and threshold decryption adds an in-TEEpartial-DH-combine round for any private-data P2 / secre
@denniswon

Copy link
Copy Markdown
Author

❯ one question: are we saving blocks in our state db / jmt? why or why not?

Good question — and exactly the kind I should verify at source rather than answer from memory (I've been wrong on V2 internals three times this session). It's also directly load-bearing for the design spikes: NEWT-1972 (DA holds the block log), NEWT-1973 (reconcile fetches block deltas), and NEWT-1975 (block digest binding) all turn on where block data actually lives versus where state lives.

Let me ground it in the actual code — delegating the file-reading to keep this context clean and get file:line evidence:

⏺ Agent(Verify V2 block vs JMT storage) Sonnet 5
Done (17 tool uses · 196.1k tokens · 8m 44s)

Verified at source. Direct answer: no — blocks are not stored in the JMT. The state tree holds state (leaves); blocks live in a separate store. Here's the actual layout and the reasoning behind it.

What's where

The JMT (crates/state-tree) persists leaves only, split across the Control and Data planes (storage.rs): JMT internal nodes, values (every write keyed key_hash‖version), tombstones, a bounded root_by_version index, and a stale_log. There is no block-object table anywhere in the crate. The only block-adjacent thing is AUX_TABLE — a single opaque pointer (e.g., the operator's confirmed-tip cursor), not a log.

Block data lives in three tiers, all separate from the JMT:

  1. Sequencer — ephemeral. BlockFinalization / BlockDelta are built in memory per tick and fanned out over two bounded tokio::broadcast channels. The module doc says it outright: "The sequencer never persists state — operators and the bootnode hold the durable copies" (crates/sequencer/src/sequencer.rs:1-7).
  2. Bootnode-sidecar — the durable block log. A separate redb database (distinct file from the state tree) with three tables: block_log (height → BlockFinalization), block_deltas (height → RLP delta bytes), and tip_height, committed atomically per block (crates/bootnode-sidecar/src/storage.rs:104-153).
  3. Operator catch-up. BootnodeClient::fetch_block does GET /block/{height}/delta; the operator verifies the bootnode's Ed25519 signature over keccak256(height‖block_hash‖delta_bytes) before decoding — fail-closed, a bad/missing sig halts the FSM in Stall (crates/operator-fsm/src/reconcile.rs:22-47).

★ Insight ─────────────────────────────────────
The JMT and the block log are two different data models on purpose. A Jellyfish Merkle Tree is an authenticated state structure — it answers "what is leaf K now, and prove it" with a root + inclusion proof. A block is a state transition — an ordered delta that produced that state. Putting blocks in the JMT would conflate the state commitment with the transition history and bloat the tree with data that never needs Merkle proofs. So state goes in the JMT; the ordered transition chain goes in a plain key-value log (height → block) in the DA tier. The on-chain commitStateRoot anchors the JMT root (state), never the blocks.
─────────────────────────────────────────────────

Why it's built this way

  • The sequencer is deliberately stateless — that's the ADR's whole direction (gateway/sequencer holds no durable state), so it can't be where blocks durably live. It produces and broadcasts; the bootnode and operators persist.
  • The block log exists to serve reconcile. A lagging operator rebuilds state by fetching block deltas by height and replaying them — so the log is keyed by height and lives in the DA tier (the bootnode), which is precisely the reconcile source.
  • One nuance worth flagging: the JMT does retain versioned leaf history (values keyed key_hash‖version, no eviction found) — so it holds per-version state, which people sometimes mistake for "storing blocks." It doesn't: it stores the results of applying block deltas, not the blocks (order, delta framing, roots) themselves.

Why this matters for the design spikes

This separation is exactly why five of the nine gates cluster around the block log / DA / reconcile rather than the state tree:

  • NEWT-1972 (DA HA) — the block log lives in a single bootnode-sidecar redb (one file, one process, not replicated, not in the shipped image). Reconcile depends entirely on it → SPOF on the liveness path.
  • NEWT-1973 (multi-block replay) — catch-up fetches block/{height}/delta one height at a time; a 2+-block gap isn't handled.
  • NEWT-1975 (cert binds replay package) — the block log stores block_deltas as RLP bytes, but is the operator-signed digest bound to that exact versioned delta? (block_hash currently excludes block_delta_hash, which omits the version.)
  • NEWT-1968 (self-sufficient delta) — do the stored delta bytes carry versions so replay reproduces the exact root?

So the short version: JMT = authenticated state; the bootnode block log = the transition chain; the sequencer = ephemeral producer — and the fact that the block log is a single under-built store is the root of the DA/reconcile design gates.

One honesty note from the verification: the bootnode's HTTP router (http.rs) routes were inferred from the client call sites and module docs rather than read line-by-line, but the storage schema (the three-table block log) and the sequencer's ephemerality were confirmed by direct read. If you want, I can nail down the exact router surface — though it's not load-bearing for the answer.

@denniswon

Copy link
Copy Markdown
Author

Audit complete. Here's the full picture, grounded in the roadmap (docs/v2/ROADMAP.md, the inventory of record), the post-roadmap commit history on ARCHITECTURE.md, and the live Linear state.

V2: what is still design-blocked, not yet task-split

The headline: the design-blocked frontier moved four days ago. The roadmap's SP-0 adjudication work (X1–X7) has already landed — since 2026-07-12 a commit series closed the contradictions (86d3d847 X1/X4, 596444b6 X2, 361ec368 X3, 5839ec03 X4, 46331151 X6, d79db229 X7, 6ddd249c X1–X7 reconciliation fix-up) and added per-section build-status markers (437415c5). Those are no longer what blocks tasking. What blocks tasking now sits in three distinct tiers.

Tier 1 — Active design gates: the consensus-target redesign (NEWT-1964)

This is the live design wall. The stateless-sequencer + per-block-attest ADR got a codex adversarial verdict of "Needs changes" on 2026-07-15, which spawned 9 design-gate tickets. All 5 implementation phases (NEWT-1965–1969) sit in Backlog and may not start until their gates close — this is the largest body of V2 work currently frozen behind brainstorming/design:

┌───────────┬────────────────────────────────────────────────────────────────────────────────────┬──────────────┬──────────────────────┐
│ Gate │ Question it must answer │ Blocks │ Status │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1977 │ End-to-end cross-operator execution determinism (the attest linchpin) │ NEWT-1967 │ Done 07-15 │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1971 │ Liveness: per-operator stall must not become a global halt under <66% attest │ NEWT-1967 │ Done today (this │
│ │ │ │ branch) │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1972 │ Quorum-grade DA / reconcile availability — bootnode HA, non-optional, │ NEWT-1967 │ Open │
│ │ withholding-resistant │ │ │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1974 │ Per-block BLS on the critical path: latency/throughput budget, measured not argued │ NEWT-1967 │ Open │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1978 │ Relocating admission/state-derived checks off the gateway tree (the │ NEWT-1967 │ Open │
│ │ stateless-gateway cut) │ │ │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1973 │ Deterministic multi-block gap replay (one-block rollback is insufficient) │ NEWT-1968 │ Open │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1975 │ Block cert must bind a self-sufficient replay package (delta/evidence hash, JMT │ NEWT-1969 │ Open │
│ │ versions, P3 inputs) │ │ │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1976 │ P3 ingestion topology (chain-agnostic?), canonical source-set, evidence bounds │ NEWT-1966 │ Open │
├───────────┼────────────────────────────────────────────────────────────────────────────────────┼──────────────┼──────────────────────┤
│ NEWT-1979 │ Band C: on-chain anchoring/verifiability of per-block certs — the light-client + │ target │ Open │
│ │ challenger story │ design │ │
└───────────┴────────────────────────────────────────────────────────────────────────────────────┴──────────────┴──────────────────────┘

Two structural notes: NEWT-1972 subsumes the roadmap's "ratify the bootnode SPOF in writing or promote hot standby" open decision — it escalates it to a hard design requirement. And NEWT-1974 is design-blocked on measurement: it cannot close before SP-1's load/latency harness exists, coupling this tier to activation.

Tier 2 — Design holes the roadmap names inside sub-projects

These are places where the roadmap explicitly says the design paragraph is missing — the reason no task split exists:

┌───────────────────────────┬────────────────────────────────────────────────────────────────────────────┬─────────────────────────────┐
│ Component │ The missing design │ Where recorded │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ Resume / │ No wire type, RPC, or FSM state exists; §6/§13 both depend on it. "Design │ SP-1; ARCHITECTURE.md │
│ AwaitGatewayRecovery │ and build" │ §6/§13 build-status markers │
│ handshake │ │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ │ The two authorization rules: 0x01 link needs dual consent (policy-client │ SP-2 ("the remaining design │
│ 0x01/0x03 sender rules │ owner + subject); 0x03 keys hash coordinates, not values, so authorization │ work is the two sender │
│ │ must derive from the coordinate preimage — neither rule is designed │ rules") │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ NEWT-1933 stranded-cursor │ Version-rejected batch never advances the 0x00 cursor — "needs a protocol │ SP-2 │
│ │ decision" │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ TLSN fetch mode │ Who runs the attester, registration, key trust, notary registry, staleness │ SP-5 item 6 │
│ │ bound — open since v1 (NEWT-507), for years │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ P3 per-field aggregation │ X6 adjudicated as "not built"; topology/source-set design gated on │ SP-5 items 2/5 │
│ semantics │ NEWT-1976; ModalAggregator eviction/TTL a stated precondition │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ V2 fault-detection │ The challenger successor: no architecture doc exists; and if challenging │ │
│ service │ is permissionless, a reference detector + incentive specification are │ SP-6 │
│ │ required — "permissionless is not a detector" │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ NEWT-1818 consent │ Per-policy/per-eval consent mechanism — launch blocker for third-party │ SP-7 │
│ │ clients; a Phase-1 mainnet is otherwise outside its own threat model │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ Honest-host decision │ Ship threshold mode, land NEWT-1191, or ratify the honest-host assumption │ SP-7 │
│ │ explicitly — an unmade three-way decision │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ NEWT-1500 PCR0 liveness │ Nothing proves a committed PCR0 came from a live enclave post-teardown — │ SP-7 │
│ │ research-level open │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ Operator coordination │ Release-level skew at cutover: no minimum-version gate, no │ SP-8 │
│ protocol │ coordinated-restart design — "design the coordination; do not discover it" │ │
├───────────────────────────┼────────────────────────────────────────────────────────────────────────────┼─────────────────────────────┤
│ Critical-path choice │ P3 chain vs resolver chain — "measured, not guessed"; pending the latency │ Work breakdown │
│ │ harness │ │
└───────────────────────────┴────────────────────────────────────────────────────────────────────────────┴─────────────────────────────┘

Tier 3 — Specced at prose level, never expanded into task issues

The board pathology the roadmap documents (113 of 135 V2 issues parentless) still holds for the milestone epics:

  • NEWT-1243 (M3, dApp-facing contracts) — the one epic whose blocker has now cleared: X1 was its gate and X1 is adjudicated. SP-3a's five items (NewtonMessage schema, V2 AttestationValidator, relayer pair, NewtonPolicyClientBase, NEWT-1735) are specified to build-ready detail in the roadmap — yet its children remain unrelated signed-read follow-ups; the real scope has zero task issues. This is now expandable, not design-blocked.
  • NEWT-1244 (M4, resolver/SDK/observability) — still verbatim "expand when the plan is written." §10.3 prose exists; X2 is adjudicated; but the capability-token format, hot-standby/failover, and read-model schema have no design beyond prose.
  • NEWT-1245 (M5) / NEWT-1246 (M6) — both still verbatim "expand when the plan is written." SP-8/SP-9 now provide the skeleton, but M5's hard missing piece is the operator-coordination design above, and M6's "before September" target is dead per the roadmap's own costing.

The boundary — what is not design-blocked

For contrast, so this reads as an audit rather than a panic: SP-1 activation (Docker/config/wiring, minus the Resume handshake), SP-3a contract build (X1 closed — expandable today), NEWT-1965 (P3 schema-driven aggregation — fully specced, a launch blocker, implementable now; only its sibling 1966 waits on gate 1976), and the SP-0 residue (NEWT-1934 lint, orphaned E2Es, main-sync — mechanical, and the main-sync CI gate already landed in c0a7dcaf/5e0a2347).

★ Insight ─────────────────────────────────────

  • The audit's shape confirms the roadmap's own meta-lesson: every Tier-2 item is a process or protocol that must run (a handshake, a detector, an incentive loop, a coordination ceremony) — exactly the category a file:line audit is structurally blind to, which is why these survived five parallel source audits and only surfaced via adversarial review lanes.
  • Note the gate-freshness inversion: the X1–X7 contradictions (July 12) closed within four days, while the NEWT-1964 gates (July 15) already have 2 of 9 closed. Design gates with named owners, Band severity, and a merge-blocking artifact rule are clearing at ~1/day — the un-expanded epics, which have none of those properties, haven't moved in two months. The forcing function, not the difficulty, predicts which design debt gets paid.
    ─────────────────────────────────────────────────

Net: the brainstorm-blocked set today = 6 open Band-A gates + 1 Band-C gate on the consensus redesign (freezing NEWT-1966–1969), plus 11 named design holes inside SP-1/2/5/6/7/8, plus M4/M5/M6 epics awaiting plans. The single highest-leverage unblock is closing the remaining NEWT-1964 gates — they freeze the entire consensus phase-set, and one of them (1974) transitively demands the latency harness that also gates SP-9.

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