+ Hachi [NOZ26] is a lattice-based multilinear polynomial commitment over the
+ power-of-two cyclotomic ring Z_q[X]/(X^{2^α}+1). The opening is a
+ single line of nine reductions — starting at the quadratic-form evaluation
+ (QuadEval) and ending at a bare multilinear-evaluation claim on the committed table.
+ This map places every protocol-bearing file on that line, marks the bridges and
+ subfolders, and shows exactly what is proven or in progress.
+
Counts are over the 22 files carrying definitions or theorems (the six folder umbrella
+ re-export files are excluded). Status reflects real sorry proof terms, not the word
+ appearing in a docstring.
+ 2
+ Subprotocol step — a numbered link that runs a real interactive round (a message and/or a challenge) and carries a coordinate-wise-special-soundness (CWSS) extractor.
+
+
+
+ Bridge / adapterbridge — a zero-round link that only reshapes the statement to glue one subprotocol's output relation to the next one's input. Sound for any structure.
+
+
+ guarded
+ Guarded verifier — its runtime check reads data the next statement type drops (a sumcheck target, a packed value); it may failure at runtime and composes via ▷ᵍ.
+
+
+ A / B
+ Subfolder — the accented segment of each file path is its Hachi/ subfolder; every subfolder is one paper subprotocol.
+
+
+
+
Proof status
+
+ proven
+ Sorry-free and complete. The QuadEval subprotocol (Lemma 8) is even axiom-clean — only propext / Classical.choice / Quot.sound.
+
+
+ wip
+ A real skeleton: types, relations, verifier and package are defined and typecheck; the CWSS proof (and sometimes the encoding defs) are sorry.
+
+
+ planned
+ Named in the composition roadmap but not yet a file — future heads, tails, and the completeness layer.
+
+
+
+
+
+
+
+
+
+
+ The composed opening
+
The reduction line — QuadEval → evaluation claim
+
Top to bottom is one opening of an Rq-committed multilinear polynomial, exactly as
+ composed in Composition.lean (openingChain). Each link reduces
+ one relation to the next; witnesses everywhere carry the escape budget · ⊕ E
+ (threaded by Escape.lean). The final link outputs a bare multilinear-evaluation
+ claim mle[w̃](a) = y′ on the committed table.
+
+
+
+
+
+
+
+
+
+ Off the line
+
The substrate & the cross-cutting machinery
+
These files are not links in the chain, but the chain cannot stand without them: the proven mathematical
+ base the line opens against, and the wiring that composes and threads it.
+
+
Proven substrate — the commitment & algebra the line opens against
+
+
Cross-cutting — threading, shared encoding, composition & the scheme interface
+
+
+
+
+
+
+
+
+ Complete accounting
+
Every file in Hachi/
+
All 28 files, including the six folder umbrellas. Filter by status; the row column is the
+ position on the reduction line above.
+
+
+
+
+
+
+
+
+
+
+
File
Role
Paper
Row
Status
sorry
+
+
+
+
+
+
+
+
+
+
+
+ Still to do
+
Beyond the current line
+
Work named in the Composition.lean roadmap that has no file yet — the outer interface,
+ the completeness layer, and the one piece of generic machinery the guarded seams wait on.
+
+
+
+
+
+
+
+
+
diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean
new file mode 100644
index 0000000000..9e6f69695d
--- /dev/null
+++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean
@@ -0,0 +1,77 @@
+/-
+Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Tobias Rothmann
+-/
+import ArkLib.OracleReduction.Prelude
+
+/-!
+ # Escape-threaded relations (`Set.withEscape`)
+
+ Protocol-agnostic plumbing for **escape threading** in composed special-soundness chains
+ (Hachi [NOZ26] §4.3+; design decision G1 of the sumcheck-track plan).
+
+ In a composed reduction chain, a downstream extractor may fail to produce a "real" witness and
+ instead produce a cryptographic **escape** — e.g. a binding break of a commitment introduced in
+ the middle of the chain (Hachi's `w̃`-commitment of Figure 4, whose collision is a Module-SIS
+ solution via weak binding, [NOZ26] Remark 2 / Lemma 7). Composed extraction feeds each
+ extractor's output into the *previous* seam relation, so every relation upstream of the escape's
+ origin must have a home for it. `Set.withEscape` widens a relation `Set (S × W)` to
+ `Set (S × (W ⊕ E))` by adjoining an escape set `esc : Set E` on the right summand.
+
+ Crucially, `esc` is **statement-independent**: an MSIS/collision solution is checkable against
+ the (parametric) commitment key alone, so escapes pass through statement maps trivially, and the
+ escape branch of every seam extractor is the identity `Sum.inr`.
+
+ ## References
+
+ * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear
+ Polynomial Commitments over Extension Fields*][NOZ26]
+-/
+
+namespace Set
+
+variable {S W E : Type*}
+
+/-- Widen a relation by an escape disjunct: a witness is either a real witness `w : W` related to
+the statement by `rel`, or an escape `e : E` in the statement-independent escape set `esc`. -/
+def withEscape (rel : Set (S × W)) (esc : Set E) : Set (S × (W ⊕ E)) :=
+ {p | match p with
+ | (s, .inl w) => (s, w) ∈ rel
+ | (_, .inr e) => e ∈ esc}
+
+@[simp]
+theorem mem_withEscape_inl (rel : Set (S × W)) (esc : Set E) (s : S) (w : W) :
+ (s, Sum.inl w) ∈ rel.withEscape esc ↔ (s, w) ∈ rel := Iff.rfl
+
+@[simp]
+theorem mem_withEscape_inr (rel : Set (S × W)) (esc : Set E) (s : S) (e : E) :
+ (s, Sum.inr e) ∈ rel.withEscape esc ↔ e ∈ esc := Iff.rfl
+
+/-- The language of an escape-widened relation: a statement is in the language iff it is in the
+original language, or *any* escape exists (escapes are statement-independent, so a single escape
+puts every statement in the widened language). This is the formal price of escape threading: the
+widened acceptance condition is meaningful *relative to the extractor structure*, exactly as the
+MSIS disjuncts of Hachi's `relIn` already are. -/
+theorem mem_withEscape_language_iff (rel : Set (S × W)) (esc : Set E) (s : S) :
+ s ∈ (rel.withEscape esc).language ↔ s ∈ rel.language ∨ esc.Nonempty := by
+ simp only [Set.mem_language_iff]
+ constructor
+ · rintro ⟨w | e, hw⟩
+ · exact Or.inl ⟨w, hw⟩
+ · exact Or.inr ⟨e, hw⟩
+ · rintro (⟨w, hw⟩ | ⟨e, he⟩)
+ · exact ⟨Sum.inl w, hw⟩
+ · exact ⟨Sum.inr e, he⟩
+
+/-- Degeneration: widening by the empty escape set over an empty escape type loses nothing —
+membership is exactly membership of the underlying relation through `Sum.inl`. Together with
+`Empty`'s emptiness this witnesses that the escape-threaded chain generalizes the un-threaded
+one. -/
+theorem withEscape_empty_iff (rel : Set (S × W)) (s : S) (w : W ⊕ Empty) :
+ (s, w) ∈ rel.withEscape (∅ : Set Empty) ↔ ∃ w', w = Sum.inl w' ∧ (s, w') ∈ rel := by
+ rcases w with w' | e
+ · simp
+ · exact e.elim
+
+end Set
diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean
new file mode 100644
index 0000000000..6d3959f4d7
--- /dev/null
+++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean
@@ -0,0 +1,221 @@
+/-
+Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Tobias Rothmann
+-/
+import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package
+
+/-!
+ # Guarded verifiers and guarded CWSS composition (`GCWSSPackage`, `▷ᵍ`)
+
+ **Skeleton of milestone B4** of the Hachi sumcheck track (see
+ `HACHI_SUMCHECK_TRACK_PLAN.md` §2): coordinate-wise special soundness (CWSS) composition
+ where the *left* factor may **reject at runtime**.
+
+ ## Why guarded verifiers
+
+ The existing composition machinery (`Verifier.append_coordinateWiseSpecialSound`,
+ `CWSSPackage.append` = `▷`) requires the left verifier to be *pure*: its verdict is a
+ deterministic total function of statement and transcript, with all acceptance conditions living
+ in the output **relation**. This works whenever the data a check reads survives into the output
+ statement (the `QuadEval` pattern). It fails exactly where a runtime check reads *sent or input*
+ data that the downstream statement type **drops** — in Hachi:
+
+ 1. each sumcheck round's check `gᵢ(0) + gᵢ(1) = target` (the old target is dropped by the next
+ round's statement) — [NOZ26] Figure 6;
+ 2. the final-evaluation check against the last sumcheck targets — [NOZ26] Figure 7 tail;
+ 3. the §4.5 recursion handoff's trace check (the next-iteration statement type is pinned to
+ `QuadEvalStatement`, which cannot retain it).
+
+ A **guarded** verifier `if check stmt tr then pure (out stmt tr) else failure` is the faithful
+ model (`failure` is native: the verifier monad is `OptionT (OracleComp _)`). Its acceptance
+ probability is `0` on the `failure` branch, so on an *accepting* tree every leaf has
+ `check = true` — which is exactly the paper's "valid transcripts" premise, and which the
+ guarded composition theorem below feeds to the left extraction.
+
+ ## Contents
+
+ * `Verifier.IsGuardedWith` / `Verifier.IsGuarded` — the guard predicate (`Bool`-valued check,
+ design decision G3); purity is the `check := fun _ _ => true` special case
+ (`IsGuarded.of_isPure`).
+ * `Verifier.IsGuarded.append` — closure of guardedness under `Verifier.append` (**sorried**;
+ B4.4: composite check `check₁ s tr.fst && check₂ (out₁ s tr.fst) tr.snd`, mirroring
+ `Verifier.IsPure.append`).
+ * `Verifier.append_coordinateWiseSpecialSound_of_guardedLeft` — the guarded binary CWSS append
+ (**sorried**; B4.3: transplant of the pure proof with two deltas — (i) rewrite the composed
+ run via a guarded `append_run` lemma and dismiss the `check = false` branch against
+ acceptance-probability `1` vs `failure`'s probability `0`; (ii) certify left-leaf outputs in
+ `rel₂.language` via a guarded `accepting_of_mem`).
+ * `GCWSSPackage` — the guarded analogue of `CWSSPackage` (`isPure` ↝ `isGuarded`), with
+ `CWSSPackage.toGuarded` and the composition `GCWSSPackage.append` = infix `▷ᵍ`.
+
+ A guarded n-ary `seqCompose` variant (B4.4) is deliberately not skeletonized here: the Hachi
+ composition builds its guarded loop by *recursion over binary `▷ᵍ`*
+ (`ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean`), which only needs the binary
+ theorem.
+
+ ## References
+
+ * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear
+ Polynomial Commitments over Extension Fields*][NOZ26]
+-/
+
+noncomputable section
+
+open OracleComp OracleSpec ProtocolSpec
+
+namespace Verifier
+
+variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn StmtOut : Type}
+ {n : ℕ} {pSpec : ProtocolSpec n}
+
+/-- A verifier is **guarded with** a `Bool`-valued `check` and a deterministic output map `out` if
+its verdict is `pure (out stmt tr)` when the check passes and `failure` otherwise. This is the
+faithful model of a verifier that rejects at runtime (design decision G3 of the sumcheck-track
+plan: `Bool`-valued checks; decidable-`Prop` consumers use `decide`). -/
+def IsGuardedWith (V : Verifier oSpec StmtIn StmtOut pSpec)
+ (check : StmtIn → FullTranscript pSpec → Bool)
+ (out : StmtIn → FullTranscript pSpec → StmtOut) : Prop :=
+ ∀ stmt tr, V.verify stmt tr = if check stmt tr then pure (out stmt tr) else failure
+
+/-- A verifier is **guarded** if it is guarded with *some* check and output map. Purity is the
+special case `check := fun _ _ => true` (`IsGuarded.of_isPure`). -/
+class IsGuarded (V : Verifier oSpec StmtIn StmtOut pSpec) : Prop where
+ is_guarded : ∃ check out, V.IsGuardedWith check out
+
+/-- Every pure verifier is guarded, with the trivially-true check. -/
+theorem IsGuarded.of_isPure (V : Verifier oSpec StmtIn StmtOut pSpec) (h : V.IsPure) :
+ V.IsGuarded := by
+ obtain ⟨f, hf⟩ := h.is_pure
+ exact ⟨fun _ _ => true, f, fun stmt tr => by simp [hf stmt tr]⟩
+
+instance (V : Verifier oSpec StmtIn StmtOut pSpec) [h : V.IsPure] : V.IsGuarded :=
+ IsGuarded.of_isPure V h
+
+section Append
+
+variable {Stmt₁ Wit₁ Stmt₂ Wit₂ Stmt₃ Wit₃ : Type}
+ {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n}
+ [∀ i, SampleableType (pSpec₁.Challenge i)]
+ {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp))
+ {rel₁ : Set (Stmt₁ × Wit₁)} {rel₂ : Set (Stmt₂ × Wit₂)} {rel₃ : Set (Stmt₃ × Wit₃)}
+
+/-- Guardedness is closed under `Verifier.append`: the composite check runs the left check on the
+transcript prefix and, if it passes, the right check on the suffix from the left output.
+
+**Sorried (B4.4).** Proof plan: mirror `Verifier.IsPure.append`
+(`OracleReduction/Composition/Sequential/IsPure.lean`) — destructure both guard witnesses, take
+`check := fun s tr => check₁ s tr.fst && check₂ (out₁ s tr.fst) tr.snd` and
+`out := fun s tr => out₂ (out₁ s tr.fst) tr.snd`, and normalize
+`Verifier.append`'s bind with `failure_bind`/`pure_bind` under the two `if`-splits. -/
+theorem IsGuarded.append (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁)
+ (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) (h₁ : V₁.IsGuarded) (h₂ : V₂.IsGuarded) :
+ (V₁.append V₂).IsGuarded := by
+ sorry
+
+/-- **Guarded binary CWSS append (skeleton of B4.3, the core of milestone B4).** Coordinate-wise
+special soundness is preserved by `Verifier.append` when the left factor is merely *guarded*
+(rather than pure).
+
+**Sorried.** Proof plan (transplant of `Verifier.append_coordinateWiseSpecialSound`,
+`Composition.lean`, with two deltas):
+1. A guarded left-run lemma `append_run_guardedLeft`:
+ `(V₁.append V₂).run stmt (tr₁ ++ₜ tr₂) = if check₁ stmt tr₁ then V₂.run (out₁ stmt tr₁) tr₂
+ else failure` (mirror of `append_run_pure_left`, plus `failure_bind`). On an accepting leaf
+ (`Pr = 1`), the `check₁ = false` branch contradicts `failure`'s acceptance probability `0`
+ (rejection lemma B4.1), so every surviving leaf has `check₁ = true` and the proof is literally
+ the pure proof from there.
+2. Where the pure proof certifies each left-leaf output in `rel₂.language` via
+ `pure_accepting_of_mem`, use its guarded analogue fed by the `check₁ = true` fact from delta 1.
+ (Each left leaf learns `check₁ = true` from *some* suffix transcript — the same nonemptiness
+ the pure proof already extracts via `LeafPath.exists_of_mem_fullTranscripts`.)
+
+The tree machinery (`appendSplit` and friends) is untouched. -/
+theorem append_coordinateWiseSpecialSound_of_guardedLeft
+ (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂)
+ (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂)
+ (hV₁ : V₁.IsGuarded)
+ (h₁ : V₁.coordinateWiseSpecialSound init impl D₁ rel₁ rel₂)
+ (h₂ : V₂.coordinateWiseSpecialSound init impl D₂ rel₂ rel₃) :
+ (V₁.append V₂).coordinateWiseSpecialSound init impl
+ (CWSSStructure.append D₁ D₂) rel₁ rel₃ := by
+ sorry
+
+end Append
+
+end Verifier
+
+namespace CoordinateWise
+
+variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type}
+
+/-- A **bundled guarded coordinate-wise-special-sound reduction**: `CWSSPackage` with the purity
+witness relaxed to a guardedness witness. Guarded packages compose with `GCWSSPackage.append`
+(infix `▷ᵍ`); a pure package enters the guarded world via `CWSSPackage.toGuarded`. -/
+structure GCWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp))
+ (StmtIn WitIn StmtOut WitOut : Type) {n : ℕ} (pSpec : ProtocolSpec n) where
+ /-- The package's verifier (may reject at runtime). -/
+ verifier : Verifier oSpec StmtIn StmtOut pSpec
+ /-- The coordinate-wise structure the verifier is special sound for. -/
+ struct : CWSSStructure pSpec
+ /-- The input relation. -/
+ relIn : Set (StmtIn × WitIn)
+ /-- The output relation. -/
+ relOut : Set (StmtOut × WitOut)
+ /-- The verifier is guarded: its verdict is a deterministic function of statement and
+ transcript behind a `Bool` check. Needed to place this package as the left factor of a guarded
+ append. -/
+ isGuarded : verifier.IsGuarded
+ /-- The certificate: `verifier` is coordinate-wise special sound for `struct`, reducing `relIn`
+ to `relOut`. -/
+ isCWSS : verifier.coordinateWiseSpecialSound init impl struct relIn relOut
+
+namespace GCWSSPackage
+
+variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)}
+
+/-- Forget purity: every (pure) `CWSSPackage` is a `GCWSSPackage` with the trivially-true
+check. -/
+def _root_.CoordinateWise.CWSSPackage.toGuarded
+ {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n}
+ (L : CWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec) :
+ GCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where
+ verifier := L.verifier
+ struct := L.struct
+ relIn := L.relIn
+ relOut := L.relOut
+ isGuarded := Verifier.IsGuarded.of_isPure L.verifier L.isPure
+ isCWSS := L.isCWSS
+
+/-- **Compose two guarded packages along a matching seam** (`hseam` discharged by `rfl`): the
+guarded analogue of `CWSSPackage.append`/`▷`. The composed verdict is guarded by the conjunction
+of both checks (`Verifier.IsGuarded.append`), and the composed certificate is the guarded binary
+append theorem `Verifier.append_coordinateWiseSpecialSound_of_guardedLeft` (both currently
+sorried B4 milestones — this definition is the *interface* the Hachi chain composes through).
+Written infix as `L₁ ▷ᵍ L₂`. -/
+def append {StmtA WitA StmtB WitB StmtC WitC : Type}
+ {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n}
+ [∀ i, SampleableType (pSpec₁.Challenge i)]
+ (L₁ : GCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁)
+ (L₂ : GCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂)
+ (hseam : L₁.relOut = L₂.relIn := by rfl) :
+ GCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) where
+ verifier := L₁.verifier.append L₂.verifier
+ struct := L₁.struct.append L₂.struct
+ relIn := L₁.relIn
+ relOut := L₂.relOut
+ isGuarded := Verifier.IsGuarded.append L₁.verifier L₂.verifier L₁.isGuarded L₂.isGuarded
+ isCWSS := by
+ have h₂ := L₂.isCWSS
+ rw [← hseam] at h₂
+ exact Verifier.append_coordinateWiseSpecialSound_of_guardedLeft init impl
+ L₁.verifier L₂.verifier L₁.struct L₂.struct L₁.isGuarded L₁.isCWSS h₂
+
+end GCWSSPackage
+
+@[inherit_doc GCWSSPackage.append]
+scoped infixr:65 " ▷ᵍ " => GCWSSPackage.append
+
+end CoordinateWise
+
+end
diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean
new file mode 100644
index 0000000000..e8d2d27860
--- /dev/null
+++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean
@@ -0,0 +1,106 @@
+/-
+Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Tobias Rothmann
+-/
+import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound
+
+/-!
+ # Scalar single-challenge-round CWSS assembly (generic building block)
+
+ **Skeleton of milestone F4.1** of the Hachi sumcheck track (`HACHI_SUMCHECK_TRACK_PLAN.md`
+ §5): the `(ℓ = 1, k)` twin of `CoordinateWise.SingleRound` (which stays pinned to the
+ vector-challenge `(ℓ, k) = (2^r, 2)` fold shape of `QuadEval`).
+
+ Several Hachi subprotocols are two-round reductions "one prover message, then one **scalar**
+ challenge" whose special soundness is plain `k`-special soundness (`ℓ = 1`) at various `k`:
+
+ * the HMZ25 lift (Figure 4 / Lemma 9): message `t = Com(w̃)`, challenge `α ← F`, `k = 2d`;
+ * each paired sumcheck round (Figure 6 / Lemma 11): message = round-polynomial pair,
+ challenge `aᵢ ← F`, `k = max-degree + 1`.
+
+ This file provides their shared wire format `pSpecScalar`, the CWSS structure
+ `scalarStructure k` (= `CWSSStructure.ofSpecialSound`, arity `k`), the per-round instances,
+ and the **sorried** generic assembly `coordinateWiseSpecialSound_of_mkWitness_scalar`: any pure
+ statement-extending verifier of this shape is CWSS for `scalarStructure k`, given only a witness
+ assembler `mkWitness` that turns `k` per-branch `relOut`-witnesses at *pairwise-distinct*
+ challenges into a `relIn`-witness.
+
+ Proof plan (F4.1): transplant `SingleRound.lean`'s tree readers/shape recovery at arity `k`
+ (`Fin.cast` along `1*(k−1)+1 = k`); at `ℓ = 1` the star machinery collapses to injectivity of
+ the challenge family (`isSpecialSoundFamily_one_iff_injective`), so `hmk` receives plain
+ `Function.Injective fam` instead of `StarAt`.
+
+ ## References
+
+ * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear
+ Polynomial Commitments over Extension Fields*][NOZ26]
+-/
+
+open OracleComp OracleSpec ProtocolSpec CoordinateWise
+
+namespace CoordinateWise.ScalarRound
+
+/-- The two-round scalar-challenge protocol: the prover sends a message `Msg` (round 0,
+`P_to_V`), the verifier replies with a single scalar challenge `C` (round 1, `V_to_P`). -/
+@[reducible] def pSpecScalar (Msg C : Type) : ProtocolSpec 2 :=
+ ⟨!v[.P_to_V, .V_to_P], !v[Msg, C]⟩
+
+variable {Msg C : Type}
+
+/-- The scalar-round CWSS structure at soundness parameter `k`: a single challenge coordinate
+(`ℓ = 1`) over the alphabet `C`, i.e. plain `k`-special soundness — the shape of Hachi
+Lemmas 9 and 11. Arity `1·(k−1)+1 = k`. -/
+@[reducible] def scalarStructure (k : ℕ) (hk : 2 ≤ k) :
+ CWSSStructure (pSpecScalar Msg C) :=
+ CWSSStructure.ofSpecialSound (fun _ => k) (fun _ => hk)
+
+section Instances
+
+variable [SampleableType C] [OracleInterface Msg]
+
+/-- Hand-written 2-round instances (not auto-derived for `ProtocolSpec 2`). -/
+instance : ∀ i, SampleableType ((pSpecScalar Msg C).Challenge i)
+ | ⟨0, h⟩ => nomatch h
+ | ⟨1, _⟩ => (inferInstance : SampleableType C)
+
+instance : ∀ i, OracleInterface ((pSpecScalar Msg C).Message i)
+ | ⟨0, _⟩ => (inferInstance : OracleInterface Msg)
+ | ⟨1, h⟩ => nomatch h
+
+end Instances
+
+section Assembly
+
+variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitIn WitOut : Type} [Nonempty WitOut]
+ {σ : Type} [SampleableType C]
+
+/-- **Generic scalar-round CWSS assembly (skeleton, F4.1).** Any pure statement-extending
+verifier of the two-round scalar `pSpecScalar` is coordinate-wise special sound for
+`scalarStructure k`, provided a witness assembler `mkWitness` that turns `k` per-branch
+`relOut`-witnesses at pairwise-distinct challenges into a `relIn`-witness. This is the engine
+behind Hachi Lemma 9 (`k = 2d`, interpolation) and Lemma 11 (`k = deg + 1`, per sumcheck round).
+
+**Sorried.** Proof plan: transplant `SingleRound.coordinateWiseSpecialSound_of_mkWitness` — the
+tree at arity `k` is one message node over one challenge node over leaves (`tree_shape` at
+arity `k`); the `SS(C, 1, k)` node predicate is injectivity of the challenge family
+(`isSpecialSoundFamily_one_iff_injective` composed with the `Equiv.funUnique` decomposition of
+`scalarStructure`); branch acceptance yields per-branch `relOut`-membership via
+`mem_of_pure_accepting`. -/
+theorem coordinateWiseSpecialSound_of_mkWitness_scalar
+ (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp))
+ {k : ℕ} (hk : 2 ≤ k)
+ (V : Verifier oSpec StmtIn (StmtIn × Msg × C) (pSpecScalar Msg C))
+ (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩))
+ (relIn : Set (StmtIn × WitIn))
+ (relOut : Set ((StmtIn × Msg × C) × WitOut))
+ (mkWitness : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → WitIn)
+ (hmk : ∀ s v (fam : Fin k → C) (resp : Fin k → WitOut),
+ (∀ j, ((s, v, fam j), resp j) ∈ relOut) → Function.Injective fam →
+ (s, mkWitness s v fam resp) ∈ relIn) :
+ V.coordinateWiseSpecialSound init impl (scalarStructure k hk) relIn relOut := by
+ sorry
+
+end Assembly
+
+end CoordinateWise.ScalarRound
diff --git a/HACHI_GENERIC_RING_SWITCH_PLAN.md b/HACHI_GENERIC_RING_SWITCH_PLAN.md
new file mode 100644
index 0000000000..b842b63097
--- /dev/null
+++ b/HACHI_GENERIC_RING_SWITCH_PLAN.md
@@ -0,0 +1,1079 @@
+# Generic Ring Switch × Hachi — Reconciliation & Implementation Plan
+
+Target: extend `origin/feat/generic-ring-switch`'s `RingSwitching/Generic/` layer so it
+accommodates **Hachi's §3.1 packing head** (NOZ26, ePrint 2026/156), and build that head —
+guarded, zero-challenge, CWSS — composed onto the existing sorry-free chain
+(`eval_coordinateWiseSpecialSound`, [Basic.lean:136](ArkLib/Commitments/Functional/Hachi/Basic.lean#L136)).
+This plan supersedes Phases B–D of [`HACHI_RING_SWITCHING_PLAN.md`](HACHI_RING_SWITCHING_PLAN.md)
+(the `PackingScheme`-over-`Profile` design); Phases A, E, F, G of that plan are unaffected except
+for the deltas listed in §10 (Phase 7, item 3). Every file/line/signature anchor below was re-verified on the working
+tree and on `origin/feat/generic-ring-switch` (2026-07-09); the working tree is branch
+`hachi-polynomial-quadratic-eq`.
+
+---
+
+## 0. Overview — what changes and why
+
+The branch `feat/generic-ring-switch` (author: Alexander Hicks; 3 commits over main; purely
+additive, +1351 lines across 16 files) generalizes ring switching via a new
+`RingSwitching/Generic/` layer: `RingSwitchCarrier` (packing algebra `P` + opening algebra `E`,
+everything derived from two `Basis` witnesses), `BatchingStrategy` (challenge + Schwartz–Zippel
+`separates` bound), an anchored relation chain `openingClaimRel → sliceRel → sumcheckClaimRel`,
+and a `PackedCommitment`/`DenseMLPCS` PCS interface whose soundness field is **RBR knowledge
+soundness**. Its docstrings envision Hachi as "the S8 non-domain sibling": a `BatchingStrategy`
+instance over `R_q` supplying its own `separates` proof.
+
+That roadmap mis-models Hachi. Hachi's §3 ring switch is **deterministic**: the evaluation point
+is engineered to be subfield-valued, so the reduction is one prover message `Y ∈ R_q`, one trace
+check (Theorem 2), **zero challenges, zero sumcheck, zero soundness error** — and the residual
+claim is already a native `R_q` evaluation claim consumed by the existing Fig. 3 chain. There is
+nothing to batch and nothing to relocate. Moreover the Hachi chain's security currency is CWSS
+(coordinate-wise special soundness, composed via `CWSSPackage`/`▷`), not RBR, and the repo has no
+RBR↔CWSS bridge.
+
+**The five changes, and why each is necessary:**
+
+1. **Add a deterministic "packed-claim" exit stage to `Generic/`** (new `PackedClaim.lean` +
+ relation re-anchor). *Why necessary:* the branch's pipeline is hardwired
+ claims → eq-slices → batching → sumcheck → RBR-PCS; Hachi exits after the packing check, and
+ today that exit does not exist as a stage, a relation, or a lemma. Without it, Hachi cannot be
+ an instance of the generic layer at all — it would have to fake a degenerate
+ `BatchingStrategy`, which models an identity *fold*, not a trace *check*.
+
+2. **Retarget the "S8 Hachi sibling" docs.** *Why necessary:* three docstrings on the branch
+ (Batching.lean ×2, Relations.lean sanity) present Hachi as a future non-domain *batching*
+ instance. Left standing, the branch's own roadmap contradicts the protocol it claims to host,
+ and future work would build the wrong thing. The `CommRing`-only vocabulary is right; the
+ pipeline position is wrong.
+
+3. **Close the small lattice-layer gaps** (`psi_smul`, a bundled `psiLinearEquiv`,
+ `Nontrivial ↥(fixedSubring …)`, a named unit/cancellation lemma, one index-equiv). *Why
+ necessary:* the head's check reads the ψ-coordinates of the prover message; ψ exists only as a
+ bare bijective function today (`psi_bijective`), with no scalar-compatibility lemma and no
+ bundled inverse — the coordinate map `psiInv` cannot even be written down without them.
+
+4. **Build the head itself**: the unpack algebra (`unpackPoly` + the Theorem-2-powered
+ `unpackPoly_eval` / `traceCheck_iff`), the guarded 1-message verifier, `relRingSwitch`, its
+ CWSS theorem, and a **guarded CWSS append** in the framework. *Why necessary:* (a) the head's
+ check consumes `(xt, y)`, which the downstream `PolyEvalStatement` drops — the check can live
+ neither in a downstream relation nor in a pull-back, so the verifier must be able to *reject*
+ (`failure`), and (b) every existing CWSS composition theorem
+ ([Composition.lean:414](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Composition.lean#L414))
+ requires a **totally pure** left verifier — a guarded head is inadmissible without the new
+ guarded append. The Basic.lean TODO block explicitly requests this
+ ("Guarded subprotocols need a guarded variant of `▷`", Basic.lean:256-272).
+
+5. **Compose into the chain** (`ringSwitchChain`, extending `evalChain`). *Why necessary:* the
+ deliverable is the end-to-end theorem — extension-field-style claim down to Eq. (20) — and the
+ chain's composition discipline (`CWSSPackage`, syntactic `rfl` seams at
+ `relPolyEval 𝓜(q,α) …`) imposes exact statement/relation shapes on the head that must be built
+ to fit, not adapted after the fact.
+
+**What this plan deliberately does NOT do** (and why): it does not route Hachi's proofs through
+the branch's `MultilinearPoly`-based generic lemmas. The Hachi chain speaks `CMlPolynomial`
+(computable, `Vector`-indexed: `relPolyEval`'s eval claim is
+`CMlPolynomial.eval (extractedPoly Φ base o) (s.xl ++ s.xh) = s.y`), the branch speaks
+`MultilinearPoly` (an `MvPolynomial` subtype), and CompPoly's `toMvPolynomial` bridge **has no
+eval-agreement lemma** (verified missing). Building that bridge is real work with zero payoff for
+soundness. Instead: the generic layer gets the stage + value-level lemmas in its own idiom
+(consumable by Binius later), and the Hachi head proves its pull-back natively on
+`CMlPolynomial` via the existing `PolynomialEvalSplit` machinery. The `hachiCarrier` instance
+(Phase 7) pins the correspondence; the polynomial-level bridge is recorded as optional hygiene.
+
+**Phase order and dependencies:**
+
+```
+Phase 0 (branch setup)
+ → Phase 1 (Generic/ stage + docs) [independent of 2–6]
+ → Phase 2 (lattice glue)
+ → Phase 3 (unpack algebra, the heart)
+ → Phase 4 (head reduction + CWSS)
+ → Phase 5 (guarded append, framework) [independent of 1–4; needed by 6]
+ → Phase 6 (chain assembly)
+ → Phase 7 (carrier instance + hygiene)
+```
+
+Estimated effort: 0: 0.5 d · 1: 1.5 d · 2: 1.5 d · 3: 3–4 d · 4: 2–3 d · 5: 2–3 d · 6: 1 d ·
+7: 1 d. Total ≈ 12–15 focused days.
+
+---
+
+## 1. Verified ground truth (do not re-derive; re-verify only if a step fails)
+
+### 1.1 The branch (`origin/feat/generic-ring-switch`, head `c14c1827`)
+
+- Full footprint: 16 files, +1351/−12, **zero edits to existing `RingSwitching/` files**. New:
+ `Generic/{Carrier,Packing,Batching,Recombine,Relations}.lean`, `ArkLib/Data/Module/Basis.lean`
+ (`Module.Basis.sum_smul_bijective/injective`). Modified: `ArkLib.lean` (+6 imports, generated),
+ `Data/MvPolynomial/{Degrees,Multilinear}.lean` (3 new lemmas incl. `MLE_eval_eq_sum_eqTilde`,
+ `MLE_totalDegree_le`), `Data/Probability/Instances.lean` (**breaking**:
+ `prob_schwartz_zippel_mv_polynomial` gains explicit `(d : ℕ)` arg — no callers on HEAD, safe),
+ `Binius/BinaryBasefold/Basic.lean` + `FRIBinius/{CoreInteractionPhase,Prelude}.lean` (the R7
+ `witnessNovelCoeffs` semantic fix + `biniusCommitsTo`), `references.bib`, `repo-map.md`,
+ `docs/kb/concepts/ring-switching.md` (+81, "The Generic layer").
+- **Merge into HEAD is conflict-free** (verified `git merge-tree`): only `ArkLib.lean`,
+ `references.bib`, `repo-map.md` changed on both sides, all auto-merged. Caveat: the dry run
+ tested committed HEAD; the working tree is dirty.
+- Key signatures (Carrier.lean): `RingSwitchCarrier B` with fields
+ `P E ιP ιE : Type`, `[commP commE : CommRing] [algP algE : Algebra B ·] [ntP ntE : Nontrivial ·]
+ [ftP ftE : Fintype ·]`, `packBasis : Basis ιP B P`, `openBasis : Basis ιE B E` (all registered
+ `attribute [instance]`); `packedMLE Ps = ∑ i, packBasis i • componentWise_embed_MLE B m
+ (algebraMap B car.P) (Ps i)`; `bridge_eqTilde` proven.
+- Packing.lean: `packedMLE_eval (Ps) (pt : Fin m → B) : MvPolynomial.eval (fun i => algebraMap B
+ car.P (pt i)) (car.packedMLE Ps).val = ∑ i, algebraMap B car.P ((Ps i).val.eval pt) *
+ car.packBasis i` — **proven, CommRing-only, base-embedded points only** (docstring forbids
+ assuming more). `curryFamily` curries the **first** κ variables, `h_l : ℓ = ℓ' + κ`.
+- Batching.lean: `BatchingStrategy P W` fields `Challenge [Fintype] [Nonempty]`,
+ `weight : Challenge → W → P`, `error : ℝ≥0`, `separates : ∀ s s', s ≠ s' → Pr_{c ←$ᵖ
+ Challenge}[∑ u, weight c u * s u = ∑ u, weight c u * s' u] ≤ error`. Instances
+ `gammaPowers`/`eqFold` gated `[IsDomain P] [Fintype P]` at the section level (line 108).
+- Relations.lean: `openingClaimRel`/`sliceRel`/`sumcheckClaimRel` + `sumcheckClaim_of_slices`
+ (proven); `PackedCommitment` (`commitsTo` + `commitsTo_functional`, `commitsTo_not_top` proven);
+ `DenseMLPCS` with **unfilled** `perfectCompleteness`/`rbrKnowledgeSoundness` obligations.
+- Hachi/S8 docstrings to retarget: Batching.lean module docstring bullet, `BatchingStrategy`
+ docstring (lines 60-69), sanity comment before line 246; Relations.lean:312-314.
+- `git grep coordinateWiseSpecialSound` over the branch's `RingSwitching/` is **empty**; the
+ branch's CWSS dir has only `Basic.lean` + `Composition.lean` (no `NoChallenge`, no
+ `SeqCompose`, no `SingleRound`, no `Package`).
+
+### 1.2 CWSS infrastructure (working tree — strictly ahead of every remote on these files)
+
+- Files present: `Basic, Composition, SeqCompose, NoChallenge, SingleRound, Package` (Package.lean
+ is **staged-new, working-tree only**; `origin/cwss-components-infra` lacks `ofIsEmpty`,
+ `SingleRound`, `Package` — build on THIS branch, not infra).
+- `Verifier.coordinateWiseSpecialSound (D : CWSSStructure pSpec) relIn relOut` =
+ `treeSpecialSound init impl (CWSSStructure.toShape D) relIn relOut`
+ ([Basic.lean:212](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Basic.lean#L212));
+ call shape `V.coordinateWiseSpecialSound init impl D relIn relOut`.
+- `CWSSStructure.ofIsEmpty` ([NoChallenge.lean:45](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean#L45));
+ no-challenge bridge `Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx`
+ (NoChallenge.lean:118-127) with premise
+ `h : ∀ stmtIn tr, Pr[(· ∈ relOut.language) | …V.run stmtIn tr…] = 1 → (stmtIn, e stmtIn tr) ∈ relIn`
+ and extractor `e : StmtIn → FullTranscript pSpec → WitIn` — **note: `e` sees only the
+ transcript, not the downstream witness**. For reductions whose input witness must be built from
+ the *output* witness (ours), the template is instead
+ `ReduceClaim.verifier_coordinateWiseSpecialSound`
+ ([ReduceClaim.lean:186](ArkLib/ProofSystem/Component/ReduceClaim.lean#L186), hypothesis
+ `hRel : ∀ stmtIn witOut, (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn`,
+ requires `[Nonempty WitIn]`) and `SendWitness.verifier_coordinateWiseSpecialSound`
+ (SendWitness.lean:109, non-oracle 1-message P→V) — **Phase 4 mirrors these proofs**.
+- Pure append: `Verifier.append_coordinateWiseSpecialSound` (Composition.lean:414-428), purity
+ hypothesis exactly `hV₁ : ∀ stmt tr, V₁.verify stmt tr = pure (verify₁ stmt tr)`. Helpers to
+ generalize: `append_run_pure_left` (:311-319), `pure_accepting_of_mem` (:325-332), converse
+ `mem_of_pure_accepting` (SeqCompose.lean:53-60). Generic layer: `append_treeSpecialSound`
+ (:366-375) consumes `hV₁` at ~:396 and ~:407.
+- Chain packaging: `CWSSPackage` (Package.lean:54-69; fields `verifier struct relIn relOut
+ isPure isCWSS`), `CWSSPackage.append` with autoparam seam `(hseam : L₁.relOut = L₂.relIn := by
+ rfl)`, infix `▷` (scoped `CoordinateWise`).
+- The chain is **plain `Verifier`** (no oracle statements): `bridgeVerifier : Verifier …`,
+ QuadEval `verifier : Verifier …`, composed by `Verifier.append`.
+
+### 1.3 The Hachi chain seam (working tree)
+
+- Namespace `ArkLib.Lattices.Ajtai.InnerOuter`; **never** `open ArkLib.Lattices` (ambiguous `⬝ᵥ`);
+ `open WeakBinding` for `VerifiedOpening`.
+- `PolyEvalStatement` (PolyEvalReduction.lean:81-92): fields `pp : Hachi.PublicParamsD Φ innerRows
+ (2^m) messageDigits outerRows (2^r) innerDigits dRows`, `u : Commitment Φ outerRows`,
+ `xl : Vector (Rq Φ) r`, `xh : Vector (Rq Φ) m`, `y : Rq Φ`.
+- `relPolyEval Φ base βSq γ κ` (:151-159), opening case:
+ `VerifiedOpening Φ base βSq γ κ s.pp.toPublicParams s.u o ∧
+ CMlPolynomial.eval (extractedPoly Φ base o) (s.xl ++ s.xh) = s.y`; msisB/msisD cases via
+ `ModuleSIS.relation`. `extractedPoly Φ base o : CMlPolynomial (Rq Φ) (r + m)` (:133-136).
+- `evalChain` (Basic.lean:115-128) = `bridgePackage … ▷ quadEvalPackage …`; top theorem
+ `eval_coordinateWiseSpecialSound` (Basic.lean:136-149). **Seam discipline**: a new head's
+ `relOut` must be *syntactically*
+ `relPolyEval 𝓜(q,α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m
+ messageDigits) γ (2 * ω)` with output statement exactly
+ `PolyEvalStatement 𝓜(q,α) innerRows messageDigits outerRows innerDigits dRows m r` for the
+ `rfl` autoparam to close. Section variables at Basic.lean:103-106; `b ω γ` are implicit.
+- TODO block at Basic.lean:256-272 names exactly this work; header diagram at :51-74 already
+ slots "§3.1 ring-switch packing head — planned (guarded, 1 msg)".
+
+### 1.4 Lattice layer (working tree)
+
+- `psi (α k) (a : Fin (2^α / k) → fixedSubring (R := R) α k) : Rq (powTwoCyclotomic α)`
+ (Subfield/Packing.lean:61-64) — a plain `def` (sum of `↑(a j) * Xpow (packExp α k j.val)`).
+ `psi_add` ✓ (:74), `psi_zero` ✓, `psi_bijective (α κ) (h2 : (2 : ZMod q) ≠ 0)
+ (hk : 2 * 2^κ ∣ 2^α)` ✓ (Bijectivity.lean:34). **`psi_smul` MISSING. Any bundled
+ LinearMap/LinearEquiv MISSING** (grep-verified).
+- **Theorem 2**: `traceH_psi_mul_conj (α k) (h2 : (2 : R) ≠ 0) (hk2pow : ∃ κ, k = 2^κ)
+ (hk : 2 * k ∣ 2^α) (a b : Fin (2^α / k) → fixedSubring (R := R) α k) :
+ traceH α k (psi α k a * conjAut α (psi α k b)) = (2^α / k) • ((∑ i, a i * b i : fixedSubring
+ (R := R) α k) : Rq (powTwoCyclotomic (R := R) α))` — proven
+ (TraceInnerProduct.lean:229-234). RHS is ℕ-`nsmul` of a coerced subring sum.
+- `fixedSubring (α k) : Subring (Rq (powTwoCyclotomic α))` (FixedSubring.lean:43);
+ `Fintype ↥(fixedSubring α k)` instance ✓ (Subfield/Basis.lean:305-307);
+ `Nontrivial ↥(fixedSubring …)` **not an instance** — must be derived (pattern:
+ Field.lean:288-302 `haveI`, or via `card_fixedSubring_eq q α κ h2 hk` q-explicit,
+ Cardinality.lean:99).
+- `Algebra ↥(fixedSubring α k) (Rq …)`: **free from Mathlib** (`Algebra.ofSubring`,
+ `algebraMap = S.subtype`; rfl-lemma `algebraMap_ofSubsemiring`). No ArkLib code needed.
+- Unit/cancellation: only **inline** today —
+ `IsUnit ((2^α / 2^κ : ℕ) : Rq …)` via `Nat.pow_div hκ (by norm_num), Nat.cast_pow, Nat.cast_ofNat`
+ + `(isUnit_two (powTwoCyclotomic α) h2).pow _` (TraceInnerProduct.lean:273-275); cancellation
+ pattern `rw [nsmul_eq_mul, nsmul_eq_mul] at heq; hunit.mul_left_cancel heq` (:282-283).
+ `isUnit_two` is named (Subfield/Basis.lean:227).
+- Index arithmetic: everything is literally `2 ^ α / 2 ^ κ` (Nat division), **never**
+ `2 ^ (α − κ)`; `Nat.pow_div : n ≤ m → 0 < x → x^m / x^n = x^(m-n)` (Lean core; exponent
+ inequality FIRST). `succ_le_of_two_mul_two_pow_dvd (hk : 2 * 2^κ ∣ 2^α) : κ + 1 ≤ α`
+ (Galois/Order.lean:113) supplies `κ ≤ α`.
+- ZMod-q section style: `variable (q : ℕ) [Fact (Nat.Prime q)] [NeZero q] [BEq (ZMod q)]
+ [LawfulBEq (ZMod q)]` with `q` explicit.
+- Split machinery (ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean, namespace
+ `ArkLib.Lattices.Hachi`, `[CommSemiring R]`): `splitEquiv nl nh : Fin (2^nh) × Fin (2^nl) ≃
+ Fin (2^(nl+nh))` with `(splitEquiv nl nh (x,y)).val = y.val + 2^nl * x.val` (low bits = second
+ component = first `nl` variables); `eval_eq_sum (p) (v) : eval p v = ∑ i, p.get i *
+ (monomialBasis v).get i` (:126); `monomialBasis_get` (:131);
+ `toMatrix : CMlPolynomial R (nl+nh) → PolyMatrix R (2^nl) (2^nh)` (:141) /
+ `toPolynomial : PolyMatrix R (2^nl) (2^nh) → CMlPolynomial R (nl+nh)` (:190) + round-trips
+ (:202/:209); `splitForm_monomialBasis_eq_eval (M) (xl) (xh) : splitForm M (monomialBasis
+ xl).get (monomialBasis xh).get = CMlPolynomial.eval (toPolynomial M) (xl ++ xh)` (:221-224);
+ `evalSplit_eq_eval` (:163); `monomialBasis_split` (:145). **Name trap**:
+ `Hachi.toPolynomial` (matrix reshape) ≠ CompPoly's `CMlPolynomial.toMvPolynomial` (which has
+ NO eval-agreement lemma — do not plan around it).
+- Sorries OFF this plan's path: `no_selfReciprocal_factor` (Field.lean:211),
+ `cInfNorm_psi_le` (NormBound.lean:103). Nothing here depends on either.
+
+### 1.5 Mathlib (rev v4.30.0) — exact names
+
+`Module.Basis.ofEquivFun [Finite ι] (e : M ≃ₗ[R] ι → R)` + `Basis.ofEquivFun_repr_apply`
+(`(Basis.ofEquivFun e).repr x i = e x i`, rfl) + `Basis.equivFun_ofEquivFun`;
+`Module.Basis.singleton ι R [Unique ι]` + `singleton_repr`; `Basis.equivFun_symm_apply`;
+`Finsupp.mapRange(_apply/support_mapRange)`; `MvPolynomial.eval_eq'`;
+`mem_restrictDegree_iff_degreeOf_le` (**ArkLib-local**, ArkLib/Data/MvPolynomial/Degrees.lean:183);
+`Fin.append` + `append_left/right`; `finSumFinEquiv`; `finCongr`; `Algebra.smul_def`,
+`algebraMap_smul`, `_root_.smul_eq_mul` (the `Algebra.id.` variant is deprecated);
+`Nat.pow_div {x m n} (h : n ≤ m) (hx : 0 < x)` (Lean core). Basis namespace is `Module.Basis` —
+write it qualified, per ArkLib convention.
+
+---
+
+## 2. Design decisions (each with its reason)
+
+- **G1 — Two representations, one seam.** Generic layer additions are stated in the branch's
+ idiom (`RingSwitchCarrier` + `MultilinearPoly`); the Hachi head's soundness algebra is stated
+ natively on `CMlPolynomial` using `PolynomialEvalSplit`. They are tied by the `hachiCarrier`
+ instance (Phase 7) and a documented correspondence, NOT by a proof-level bridge. *Reason:*
+ CompPoly's `toMvPolynomial` has no eval lemma; the truly generic kernel of the head's soundness
+ is two lines of `Basis.repr` linear algebra, so a representation bridge buys nothing and risks
+ much.
+- **G2 — The check is basis-coordinate-form; the trace form is an instance equivalence.** The
+ verifier's check is `y = ∑ v, w v * psiInv Y v` (all in `B := ↥(fixedSubring …)`); the paper's
+ `Tr_H(Y · σ₋₁(ψ(monomials))) = (2^α/2^κ)·y` is proven **equivalent** via `traceH_psi_mul_conj`
+ (Phase 3). *Reason:* coordinates via `Basis.repr`/`psiInv` are canonical — this kills the old
+ plan's R2 (rows-vs-columns) and D5 (σ₋₁ message twist) wholesale: the wire message is the
+ untwisted `Y`, and no `φ₀/φ₁` data is needed anywhere.
+- **G3 — Guarded verifier, `failure` on check-failure.** `verify := fun s tr => if check … then
+ pure (toPolyEvalStatement s Y) else failure`. *Reason:* the check consumes `s.xt, s.y`, which
+ `PolyEvalStatement` drops; a pure pass-through head would be unsound (nothing downstream can
+ re-impose the check), and the dummy-state convention loses the constraint in CWSS extraction.
+- **G4 — The head outputs `PolyEvalStatement` directly** (no separate σ₋₁/coercion adapter
+ reduction). *Reason:* G2 killed the twist, so the only statement work is coercion
+ `Vector B → Vector (Rq Φ)` and `y := Y` — folding it into the head's pure-branch avoids a
+ zero-round `ReduceClaim` factor and keeps the `▷` seam count minimal.
+- **G5 — Batching stays untouched; Hachi is NOT a degenerate `BatchingStrategy`.** *Reason:* a
+ `Challenge := Unit, error := 0` instance would model an identity fold of claims, not a trace
+ check — the check predicate appears nowhere in `BatchingStrategy`'s vocabulary. The honest
+ reading: `BatchingStrategy` is the *relocation* phase's design axis (DP24-only); the packing
+ stage's axis is the weight family (Phase 1).
+- **G6 — Laws as hypotheses, no new sorries in structures** (inherited from the old plan's D1
+ and the branch's own "hypotheses live on theorems" discipline). Structures carry data; `Prop`s
+ are standalone and taken as theorem hypotheses.
+- **G7 — Index conventions pinned once, in code, with `decide` examples.** All packing indices
+ are `Fin (2^α / 2^κ)`; the single named equiv `packIndexEquiv` (Phase 2) converts to
+ `Fin (2^(α−κ))` where `CMlPolynomial` arity arithmetic needs it. *Reason:* the two forms are
+ NOT defeq; every mid-proof cast is a bug factory (old plan R4).
+- **G8 — Scope guard.** Honest-prover/completeness stays at skeleton level (QuadEval precedent);
+ knowledge-error accounting, Fiat–Shamir, and the branch's S6/S7 obligations are out of scope.
+ *Reason:* matches the chain's current discipline (TODO block) and keeps this plan mergeable.
+- **G9 — The guard check is Bool-valued, defined once.** Phase 4 defines
+ `def headCheck … : Bool := decide (s.y = ∑ …)` (via `DecidableEq B`); the verifier is the Bool
+ `if headCheck … then pure … else failure`; `Verifier.IsGuarded` (Phase 5) stores a
+ `… → Bool` check so `verify_eq` matches *syntactically*; `relRingSwitch`-side proofs cross via
+ `decide_eq_true_eq`. CheckClaim's `[DecidablePred pred]` + `do guard …` convention is **not**
+ copied — only its guard/`failure` mechanics are precedent. *Reason:* three candidate
+ conventions exist in-tree; Bool is the only one that lets `IsGuarded.verify_eq` match without
+ instance-plumbing at the composition site. Decided now so Phases 4 and 5 cannot diverge.
+
+### 2.5 Delivery discipline
+
+- **Scratch files stay untracked.** Phase 0.1 commits only tracked changes plus explicitly
+ `git add`-ed new `.lean`/docs files (`git add -u` + named adds — never `git add .` at the
+ root). The four root-level `HACHI_*.md` planning notes remain untracked (CLAUDE.md: stable
+ guidance belongs in `docs/wiki/`, not ephemeral notes).
+- **PR partition:** PR-A = Phases 0–1 (Generic/ additions + docs retarget; request review from
+ the branch author, per R8). PR-B = Phase 5 (framework-only; maintainer review, per R3). PR-C =
+ Phases 2–4 + 6–7 (the Hachi head; depends on A and B).
+- **Cadence:** `./scripts/validate.sh` green at every phase boundary; `--lint` before each PR;
+ new files `git add`-ed before validation (generated `ArkLib.lean`).
+
+---
+
+## 3. Phase 0 — Branch setup (0.5 d)
+
+*Why necessary:* the Generic/ files exist only on `origin/feat/generic-ring-switch`; the CWSS
+infra (incl. `Package.lean`, staged-only) exists only on the current branch's working tree. No
+single existing ref contains both.
+
+Steps (exact):
+
+1. Commit all current working-tree changes on `hachi-polynomial-quadratic-eq` (or have the user
+ do so / stash-confirm). **Do not proceed on a dirty tree** — the merge dry-run only covered
+ committed state.
+2. `git checkout -b hachi-generic-ring-switch` (from the committed tip).
+3. `git merge origin/feat/generic-ring-switch`. Expected: clean auto-merge; the only both-sides
+ files are `ArkLib.lean`, `blueprint/src/references.bib`, `docs/wiki/repo-map.md`. If
+ `ArkLib.lean` conflicts anyway: take either side, then `git add` all new `.lean` files and run
+ `./scripts/update-lib.sh` (it regenerates `ArkLib.lean` from `git ls-files`; it **hard-fails
+ on untracked** `ArkLib/**/*.lean` — always `git add` first). Never hand-edit `ArkLib.lean`.
+4. `lake exe cache get` if needed, then `./scripts/validate.sh` — must be green before any new
+ work. Note the merge brings a breaking 4-arg `prob_schwartz_zippel_mv_polynomial` (no HEAD
+ callers — nothing to fix) and the Binius `witnessNovelCoeffs` semantic fix (HEAD does not
+ touch BinaryBasefold — nothing to fix).
+
+Acceptance: `./scripts/validate.sh` green on the merged branch;
+`git grep -l RingSwitchCarrier -- 'ArkLib/ProofSystem/RingSwitching/Generic'` returns the five
+Generic files (repo-wide the grep hits 8 paths — the five plus `Data/MvPolynomial/Multilinear.lean`
+and two docs files; that is expected, not a bad merge).
+
+---
+
+## 4. Phase 1 — Generic layer: the deterministic exit stage + docs retarget (1.5 d)
+
+### 4.1 New file `ArkLib/ProofSystem/RingSwitching/Generic/PackedClaim.lean`
+
+Imports: `ArkLib.ProofSystem.RingSwitching.Generic.Packing`. Namespace
+`RingSwitching.Generic.RingSwitchCarrier`, `variable {B : Type} [CommRing B]
+(car : RingSwitchCarrier B)`, inside `noncomputable section`, `open Module MvPolynomial
+Sumcheck.Structured` (mirror Packing.lean's header exactly).
+
+Content (names indicative; keep docstring style of the sibling files):
+
+```lean
+/-- The deterministic packed-claim check (design "step 2", packing-phase exit): the original
+claim value `y : B` is the `w`-weighted recombination of the packed carrier value's
+`packBasis`-coordinates. Hachi §3.1: `w` = tail monomials, `Y` = the one prover message;
+DP24 continues past this stage into batching + sumcheck instead. -/
+def recombineCheck (w : car.ιP → B) (Y : car.P) (y : B) : Prop :=
+ y = ∑ v, w v * car.packBasis.repr Y v
+
+/-- The residual native claim after a packing head: the packed polynomial evaluates to `Y`
+at the (base-embedded) head point. This is the deterministic exit's output anchor — for a
+carrier with `P` = the committed ring, it is already a native PCS claim. -/
+def packedClaimRel (m : ℕ) :
+ Set (((Fin m → B) × car.P) × MultilinearPoly car.P m) :=
+ { x | x.1.2 = x.2.val.eval (fun i => algebraMap B car.P (x.1.1 i)) }
+
+/-- Coordinates of an honest packed evaluation are the family's evaluations —
+`packedMLE_eval` pushed through `repr`. NB: `packBasis.repr` lands in `B`, so the RHS is the
+bare `(Ps v).val.eval pt` — the `algebraMap` in `packedMLE_eval`'s reassembly is absorbed by
+`repr`. -/
+theorem repr_packedMLE_eval {m : ℕ} (Ps : car.ιP → MultilinearPoly B m)
+ (pt : Fin m → B) (v : car.ιP) :
+ car.packBasis.repr
+ (MvPolynomial.eval (fun i => algebraMap B car.P (pt i)) (car.packedMLE Ps).val) v
+ = (Ps v).val.eval pt
+```
+
+Proof plan: rewrite with `car.packedMLE_eval`; convert each summand
+`algebraMap B car.P c * car.packBasis i` to `c • car.packBasis i`
+(`Algebra.smul_def`, symm — i.e. `simp_rw [← Algebra.smul_def]`); finish with
+**`Module.Basis.repr_sum_self`** (Mathlib LinearAlgebra/Basis/Defs.lean:265, `[Fintype ι]`:
+`b.repr (∑ i, c i • b i) = c` — verified to exist; do NOT reach for `Basis.repr_equivFun_symm`,
+which does not exist) plus a `congrFun` at `v`.
+
+```lean
+/-- Generic soundness kernel of the deterministic packing exit: if the check passes against
+the honest packed value, the weighted family claim holds. (The Binius/Hachi instances feed
+their own weight-law into `w` — hypotheses live on theorems, per the layer's discipline.) -/
+theorem recombineCheck_iff_of_packedClaim {m : ℕ} (Ps : car.ιP → MultilinearPoly B m)
+ (pt : Fin m → B) (w : car.ιP → B) (y : B) :
+ car.recombineCheck w
+ (MvPolynomial.eval (fun i => algebraMap B car.P (pt i)) (car.packedMLE Ps).val) y
+ ↔ y = ∑ v, w v * (Ps v).val.eval pt := by
+ unfold recombineCheck; simp [car.repr_packedMLE_eval]
+```
+
+Sanity section (mirror the siblings): exercise `recombineCheck` + `packedClaimRel` on
+`decoupledToyCarrier` and `towerCarrier`; one value-level `example` computing
+`repr_packedMLE_eval` on the toy carrier.
+
+*Why this change:* this is the missing stage — the branch's relation chain starts at
+`openingClaimRel` and immediately eq-decomposes toward batching; Hachi's protocol content at this
+layer is exactly (`recombineCheck`, `packedClaimRel`) and nothing else. Stating it generically
+(with the weight family `w` as the knob and the check's coordinates fixed to `packBasis.repr`)
+also gives Binius the *proven* step-2 identity for free later, replacing the old plan's
+4-field `PackingScheme` with 1 knob + 1 proven kernel lemma.
+
+### 4.2 Edit `Generic/Relations.lean` — re-anchor the chain docstring
+
+Extend the module docstring's relation-chain bullet list: `packedClaimRel` (in
+`PackedClaim.lean`) is the **shared deterministic segment**; `sliceRel → sumcheckClaimRel` is the
+**DP24/relocation route** taken only when the head point is not base-embedded. Do not change any
+existing definition. Add one `example` in the sanity section instantiating `packedClaimRel` on
+both carriers.
+
+*Why:* prevents the next reader from assuming the batching route is the only route; zero proof
+risk.
+
+### 4.3 Docs retarget (same PR)
+
+- Batching.lean module docstring + `BatchingStrategy` docstring + sanity comment (three verified
+ sites): replace "the S8 non-domain (Hachi) sibling … supply its own proven `separates`" with:
+ Hachi `R_q` is a **non-domain carrier of the packing stage only** — its head is deterministic
+ (one message + `recombineCheck`, zero challenges, zero error; see
+ `Generic/PackedClaim.lean` and `Commitments/Functional/Hachi/RingSwitch/`), and it does not
+ instantiate `BatchingStrategy`. Keep the `CommRing`-only-vocabulary sentence — it remains true
+ and load-bearing.
+- Relations.lean:312-314 sanity comment: same correction (the `DenseMLPCS (ZMod 6) 3` statability
+ example stays; it is about vocabulary, not Hachi).
+- `docs/kb/concepts/ring-switching.md` ("The Generic layer" section): add the packing-stage
+ paragraph + correct the S8 description.
+- `docs/wiki/repo-map.md`: add `Generic/PackedClaim.lean` and (Phase 4's)
+ `Hachi/RingSwitch/` entries — CLAUDE.md guardrail: same PR as the code.
+
+*Why:* the branch's written roadmap currently models Hachi as a batching instance — the exact
+misread this whole plan exists to prevent from ossifying.
+
+Acceptance (Phase 1): build green; `recombineCheck_iff_of_packedClaim` sorry-free; docstrings
+contain no remaining claim that Hachi batches.
+
+---
+
+## 5. Phase 2 — Lattice glue (1.5 d)
+
+New file `ArkLib/Data/Lattices/CyclotomicRing/Subfield/LinearEquiv.lean` (imports
+`Subfield/Packing.lean`, `Subfield/Bijectivity.lean`, `Galois/Order.lean`). Naming note: this
+phase lives in the lattice layer, whose house parameter name is `κ` (as in `psi_bijective`,
+`card_fixedSubring_eq`) — keep it here; Phases 3–6 instantiate it as `κRS` (§11's dictionary),
+since only the *chain* files have the `κ`-collision. Work in the ZMod-q section style (`variable (q : ℕ) [Fact (Nat.Prime q)] [NeZero q] [BEq (ZMod q)]
+[LawfulBEq (ZMod q)]`, q explicit) for the bundled equiv (it needs `psi_bijective`, which is
+ZMod-q); the `psi_smul` lemma can stay in the generic `[Field R]` section of Packing.lean.
+
+Deliverables, in order:
+
+1. **`psi_smul`** (append to Subfield/Packing.lean, generic section):
+ ```lean
+ theorem psi_smul (α k : ℕ) (c : fixedSubring (R := R) α k)
+ (a : Fin (2 ^ α / k) → fixedSubring (R := R) α k) :
+ psi α k (c • a) = (c : Rq (powTwoCyclotomic α)) * psi α k a
+ ```
+ Proof: unfold `psi`; `Finset.mul_sum`; per-summand `Subring` coe-of-mul + `mul_assoc`. (Pin
+ how `c • a` acts pointwise: `Pi.smul_apply` + subring `smul = mul` on the subtype —
+ if the pointwise action is not already `Mul`-defeq, state the lemma with
+ `(fun j => c * a j)` instead of `c • a`; either form serves step 2.)
+2. **Nontriviality lemmas** (as theorems producing instances, not global instances):
+ - `nontrivial_Rq_powTwoCyclotomic : Nontrivial (Rq (powTwoCyclotomic (R := ZMod q) α))` —
+ export the inline `haveI` derivation at Subfield/Field.lean:298-302 (via `Rq.equivQuotient`;
+ needs only the standing `[Fact (Nat.Prime q)]`, no `h2`/`hk`) as a named lemma. Phase 7's
+ carrier needs it for its `ntP` field.
+ - `Nontrivial ↥(fixedSubring (R := ZMod q) α (2^κ))` under `(h2) (hk)`: derive from
+ `card_fixedSubring_eq q α κ h2 hk` (`Fintype.card … = q ^ 2^κ ≥ 2` since `q` prime ⇒
+ `Fintype.one_lt_card_iff_nontrivial`), or from the subring's `0 ≠ 1` directly given the
+ ambient nontriviality above (Mathlib's `Subring` Nontrivial instance needs
+ `Nontrivial (Rq …)`, which is exactly the first lemma).
+3. **`psiLinearEquiv`**:
+ ```lean
+ noncomputable def psiLinearEquiv (α κ : ℕ) (h2 : (2 : ZMod q) ≠ 0)
+ (hk : 2 * 2 ^ κ ∣ 2 ^ α) :
+ (Fin (2 ^ α / 2 ^ κ) → fixedSubring (R := ZMod q) α (2 ^ κ))
+ ≃ₗ[fixedSubring (R := ZMod q) α (2 ^ κ)] Rq (powTwoCyclotomic (R := ZMod q) α) :=
+ LinearEquiv.ofBijective
+ ({ toFun := psi α (2 ^ κ), map_add' := psi_add α (2 ^ κ), map_smul' := … } : _ →ₗ[_] _)
+ (psi_bijective q α κ h2 hk)
+ ```
+ The `Module ↥(fixedSubring …) (Rq …)` instance is found by TC search via Mathlib's
+ `Algebra.ofSubring` (verified); `map_smul'` is `psi_smul` composed with
+ `Algebra.smul_def` + `algebraMap_ofSubsemiring` (`algebraMap = Subtype.val`-coe). Definitional
+ abbreviation: `noncomputable abbrev psiInv … := (psiLinearEquiv q α κ h2 hk).symm` with simp
+ lemmas `psiInv_psi`, `psi_psiInv` (from `LinearEquiv.symm_apply_apply` etc.).
+4. **Named unit + cancellation** (append to TraceInnerProduct.lean or the new file):
+ ```lean
+ theorem isUnit_pow_div_cast (α κ : ℕ) (hκα : κ ≤ α) (h2 : (2 : ZMod q) ≠ 0) :
+ IsUnit ((2 ^ α / 2 ^ κ : ℕ) : Rq (powTwoCyclotomic (R := ZMod q) α))
+ theorem nsmul_pow_div_cancel (α κ : ℕ) (hκα : κ ≤ α) (h2 : (2 : ZMod q) ≠ 0)
+ {x y : Rq (powTwoCyclotomic (R := ZMod q) α)}
+ (h : (2 ^ α / 2 ^ κ) • x = (2 ^ α / 2 ^ κ) • y) : x = y
+ ```
+ Proofs: lift the verified inline pattern (TraceInnerProduct.lean:273-275 and :282-283)
+ verbatim into named lemmas. Get `hκα` from `succ_le_of_two_mul_two_pow_dvd hk` (κ+1 ≤ α ⇒
+ κ ≤ α) at call sites.
+5. **`packIndexEquiv`** (the one sanctioned index cast, G7):
+ ```lean
+ def packIndexEquiv (α κ : ℕ) (hκα : κ ≤ α) :
+ Fin (2 ^ α / 2 ^ κ) ≃ Fin (2 ^ (α - κ)) :=
+ finCongr (Nat.pow_div hκα (by norm_num))
+ ```
+ Plus a `decide` example at `α = 2, κ = 1` pinning the round-trip (old plan A1's acceptance,
+ scoped down to what this plan uses).
+
+*Why each:* (1)+(3) — `psiInv` (the check's coordinate map) is `psiLinearEquiv.symm`; without
+`psi_smul` the linear map cannot be bundled and `map_sum/map_smul` (Phase 3's whole proof engine)
+are unavailable. (2) — `RingSwitchCarrier` requires `Nontrivial` for the Phase 7 instance, and
+several Phase 3 rewrites need `0 ≠ 1` in `B`. (4) — Phase 3's `traceCheck_iff` must cancel the
+`(2^α/2^κ) •` factor of Theorem 2; today that cancellation exists only inline inside another
+proof. (5) — `CMlPolynomial` arities are `2^(vars)` while ψ's index is `2^α/2^κ`; G7 mandates
+exactly one named crossing.
+
+Acceptance: all five sorry-free; `example : Fin (2^2/2^1) ≃ Fin (2^1) := packIndexEquiv 2 1
+(by omega)` compiles; `decide` example green.
+
+---
+
+## 6. Phase 3 — The unpack algebra: Theorem 2 at the polynomial level (3–4 d, the heart)
+
+New file `ArkLib/Commitments/Functional/Hachi/RingSwitch/Unpack.lean`. Imports:
+`Hachi/PolynomialEvalSplit.lean`, `Subfield/LinearEquiv.lean`, `Subfield/TraceInnerProduct.lean`.
+Namespace `ArkLib.Lattices.Hachi` (the split layer's namespace), ZMod-q section. **Header opens
+(mandatory, or nothing resolves):** `open CompPoly ArkLib.Lattices.CyclotomicModulus` — psi,
+traceH, conjAut, fixedSubring, Rq, powTwoCyclotomic all live in
+`ArkLib.Lattices.CyclotomicModulus` (mirror `Hachi/Basic.lean:97`'s open line; §12's ban is only
+on `open ArkLib.Lattices` *itself*, whose `⬝ᵥ` is ambiguous — opening the leaf namespace is
+safe and the chain already does it).
+
+Abbreviations: export a **public** `abbrev PackBase (q α κRS : ℕ) … : Type :=
+↥(fixedSubring (R := ZMod q) α (2 ^ κRS))` from this file — Phase 4's statement fields and
+Phase 7's `rfl` examples must see through it, so it cannot be `local`. `Φα`, `N := 2^α / 2^κRS`,
+`κ' := α - κRS` may stay local, with `hκα : κRS ≤ α` and crossing to `Fin (2^κ')` only via
+`packIndexEquiv` — never inline-cast. **Coercion spelling (pin once, use everywhere):** the bare
+`(↑·)` lambda does NOT elaborate (compile-verified: it degenerates to `fun x => x` and
+type-errors); define `def coeVec {n} (x : Vector (PackBase q α κRS) n) : Vector (Rq Φα) n :=
+x.map (fun b => (b : Rq Φα))` and state every lemma through `coeVec`.
+
+One prerequisite edit in `PolynomialEvalSplit.lean`: `eval_eq_sum` is stated at arity
+`nl + nh`, and `rw [eval_eq_sum]` does NOT fire at a bare arity `n` (compile-verified
+unification failure). Either generalize it to `{n : ℕ}` (the existing proof compiles unchanged —
+preferred) or invoke it as `eval_eq_sum (nl := n) (nh := 0)` (defeq `n + 0`); do not rely on
+bare `rw [eval_eq_sum]`.
+
+Deliverables, in dependency order (each a lemma; sorry-free before moving on):
+
+1. **Coe/monomial commutation.** `B`'s coe into `Rq Φα` is `SubringClass` coe (a ring hom).
+ ```lean
+ theorem monomialBasis_map_coe {n : ℕ} (x : Vector (PackBase q α κRS) n) (j : Fin (2 ^ n)) :
+ (CMlPolynomial.monomialBasis (coeVec x)).get j
+ = ((CMlPolynomial.monomialBasis x).get j : Rq Φα)
+ ```
+ Proof: `monomialBasis_get` on both sides; the RHS product of `if`-selected entries commutes
+ with the coe ring hom (`map_prod`, `apply_ite`). (If `CMlPolynomial.monomialBasis` has its own
+ `map` lemma in CompPoly, use it; otherwise `monomialBasis_get` + `Finset.prod_congr` is 10
+ lines.)
+2. **Eval at coerced points is a `B`-combination of coefficients.**
+ ```lean
+ theorem eval_coeVec {n : ℕ} (F : CMlPolynomial (Rq Φα) n) (x : Vector (PackBase q α κRS) n) :
+ CMlPolynomial.eval F (coeVec x)
+ = ∑ j : Fin (2 ^ n), (CMlPolynomial.monomialBasis x).get j • F.get j
+ ```
+ Proof: `eval_eq_sum` (per the arity note above) + step 1 + `Algebra.smul_def`/
+ `algebraMap_ofSubsemiring` to turn `↑c * F.get j` into `c • F.get j`. (Note `eval_eq_sum` is
+ stated `p.get i * (monomialBasis v).get i` — commute with `mul_comm` before the smul rewrite.)
+3. **Coordinate/eval commutation** (`psiInv` is `B`-linear — the generic kernel, instance-side):
+ ```lean
+ theorem psiInv_eval_coeVec {n : ℕ} (h2) (hk) (F : CMlPolynomial (Rq Φα) n)
+ (x : Vector (PackBase q α κRS) n) (v : Fin N) :
+ psiInv q α κRS h2 hk (CMlPolynomial.eval F (coeVec x)) v
+ = ∑ j : Fin (2 ^ n), (CMlPolynomial.monomialBasis x).get j
+ * psiInv q α κRS h2 hk (F.get j) v
+ ```
+ Proof: step 2, then `map_sum` + `map_smul` of the linear equiv, then `Pi.smul_apply` +
+ `smul_eq_mul` in `B`.
+4. **`unpackPoly`** — coefficient-wise ψ⁻¹, tail variables LAST. The compile-verified form
+ (row index = head `j` FIRST, column = tail `v` second — `toPolynomial` with target
+ `CMlPolynomial B (n + κ')` forces `PolyMatrix B (2^n) (2^κ')`):
+ ```lean
+ noncomputable def unpackPoly {n : ℕ} (h2) (hk) (F : CMlPolynomial (Rq Φα) n) :
+ CMlPolynomial (PackBase q α κRS) (n + κ') :=
+ Hachi.toPolynomial (fun (j : Fin (2 ^ n)) (v : Fin (2 ^ κ')) =>
+ psiInv q α κRS h2 hk (F.get j) ((packIndexEquiv α κRS hκα).symm v))
+ ```
+ (This lambda was type-checked by a verification agent against
+ `CMlPolynomial ↥(fixedSubring α (2^κ)) (n + (α - κ))`. If your `PolyMatrix` literal needs a
+ different constructor than a bare function, read its definition in PolynomialEvalSplit.lean
+ and keep the SAME index order: `(j, v)`, head first.) The orientation acceptance test is
+ step 5 — if anything is transposed, fix it HERE, never by casting in step 5's proof (G7).
+5. **The unpack-eval identity** (the C3 heart, replacing the old plan's
+ `traceH_packPoly_eval` at the same difficulty):
+ ```lean
+ theorem unpackPoly_eval {n : ℕ} (h2) (hk) (F : CMlPolynomial (Rq Φα) n)
+ (x : Vector (PackBase q α κRS) n) (xt : Vector (PackBase q α κRS) κ') :
+ CMlPolynomial.eval (unpackPoly h2 hk F) (x ++ xt)
+ = ∑ v : Fin (2 ^ κ'), (CMlPolynomial.monomialBasis xt).get v
+ * psiInv q α κRS h2 hk (CMlPolynomial.eval F (coeVec x))
+ ((packIndexEquiv α κRS hκα).symm v)
+ ```
+ Proof plan: LHS via `splitForm_monomialBasis_eq_eval` (with `toPolynomial_toMatrix`/the
+ round-trip to expose the matrix) = the double sum
+ `∑ v ∑ j (monomialBasis xt).get v * (monomialBasis x).get j * M j v` (note `M j v`, head
+ index first); RHS via step 3 expands to the same double sum; finish with `Finset.sum_comm` +
+ ring. All in `B` ([CommSemiring] suffices for the split machinery — verified).
+6. **Head-soundness corollary** (what Phase 4's pull-back calls):
+ ```lean
+ theorem unpackPoly_eval_of_check {n : ℕ} (h2) (hk)
+ {F : CMlPolynomial (Rq Φα) n} {x : Vector (PackBase q α κRS) n}
+ {xt : Vector (PackBase q α κRS) κ'} {Y : Rq Φα} {y : PackBase q α κRS}
+ (hY : CMlPolynomial.eval F (coeVec x) = Y)
+ (hchk : y = ∑ v, (CMlPolynomial.monomialBasis xt).get v
+ * psiInv q α κRS h2 hk Y ((packIndexEquiv α κRS hκα).symm v)) :
+ CMlPolynomial.eval (unpackPoly h2 hk F) (x ++ xt) = y := by
+ subst hY; rw [unpackPoly_eval, hchk]
+ ```
+7. **Trace-form equivalence** (paper faithfulness; Theorem 2 discharges):
+ ```lean
+ /-- Hachi's paper check (§3.1 / Theorem 2 form). Message is the UNTWISTED `Y` —
+ the σ₋₁ lives inside the trace identity, not on the wire (design G2). -/
+ def traceCheck (h2) (hk) (xt : Vector (PackBase q α κRS) κ') (Y : Rq Φα)
+ (y : PackBase q α κRS) : Prop :=
+ traceH α (2 ^ κRS) (Y * conjAut α (psi α (2 ^ κRS)
+ (fun j => (CMlPolynomial.monomialBasis xt).get (packIndexEquiv α κRS hκα j))))
+ = (2 ^ α / 2 ^ κRS) • (y : Rq Φα)
+
+ theorem traceCheck_iff_recombine (h2) (hk) (xt) (Y) (y) :
+ traceCheck h2 hk xt Y y
+ ↔ y = ∑ v, (CMlPolynomial.monomialBasis xt).get v
+ * psiInv q α κRS h2 hk Y ((packIndexEquiv α κRS hκα).symm v)
+ ```
+ (Type note: `j : Fin (2^α/2^κRS)`, so `packIndexEquiv … j : Fin (2^(α−κRS))`, and with
+ `κ'` an abbrev for `α − κRS` this is literally `Fin (2^κ')` — `monomialBasis xt |>.get`
+ accepts it with no cast; this is exactly why κ' must be an abbrev, G7.)
+ Proof plan: write `Y = psi α (2^κRS) (psiInv … Y)` (`psi_psiInv`); apply
+ `traceH_psi_mul_conj α (2^κRS) h2 ⟨κRS, rfl⟩ hk`; the RHS becomes
+ `(2^α/2^κRS) • ↑(∑ i, psiInv Y i * weights i)`; reindex the sum along `packIndexEquiv`
+ (`Equiv.sum_comp` / `Fintype.sum_equiv`) and commute the factors; conclude by
+ `nsmul_pow_div_cancel` (Phase 2.4) + `Subtype.val`-injectivity
+ (`Subtype.coe_injective` on the subring; both sides are coerced subring elements — the LHS via
+ `traceH_mem_fixed` + `mem_fixedSubring_iff` if needed, but the cleaner route is to cancel
+ first and compare inside `Rq`, then pull back along injectivity of the coe).
+
+*Why this phase:* steps 4–6 are the entire mathematical content of the head's soundness — the
+paper's Theorem 2 lifted to "the unpacked polynomial's evaluation is check-determined". Step 7 is
+what makes the formalization *the paper's protocol* (the wire check is provably the trace
+equation) rather than a lookalike; it is also where the σ₋₁ twist is discharged once and for all.
+
+Acceptance (hard): steps 1–7 sorry-free and `./scripts/validate.sh` green. Note `unpackPoly` is
+necessarily noncomputable (`psiInv` comes from `LinearEquiv.ofBijective`; `fixedSubring` itself
+is noncomputable), so a `decide`/`native_decide` evaluation of `unpackPoly_eval` is
+**infeasible, not merely impractical** — do not attempt it. Optional (30-min timebox): a
+`decide` example on the computable ingredients only (`packIndexEquiv` round-trip,
+`monomialBasis` values at `α = 2, κRS = 1`); if it doesn't land in the timebox, drop it — the
+hard acceptance stands alone.
+
+---
+
+## 7. Phase 4 — The head reduction + its CWSS theorem (2–3 d)
+
+New file `ArkLib/Commitments/Functional/Hachi/RingSwitch/Head.lean`. Imports: `Unpack.lean`,
+`PolynomialQuadraticEq/PolyEvalReduction.lean`, CWSS `NoChallenge`/`Package`. Namespace
+`ArkLib.Lattices.Ajtai.InnerOuter` (the chain's namespace; `open WeakBinding`, plus
+`open CompPoly ArkLib.Lattices.CyclotomicModulus` as in Phase 3; do NOT
+`open ArkLib.Lattices` itself). Section variables: copy Basic.lean:103-106 verbatim, **plus
+`{κRS : ℕ}`** (it is NOT in Basic.lean's list; with `autoImplicit = false` forgetting to declare
+it is a hard error) and `(hκα : κRS ≤ α) (h2 : (2 : ZMod q) ≠ 0) (hk : 2 * 2 ^ κRS ∣ 2 ^ α)` —
+**the ring-switch parameter is named `κRS` throughout**: the chain already uses `κ` for the
+challenge-set parameter (`relPolyEval … γ κ`, instantiated at `κ := 2 * ω`). This collision is
+the old plan's R5; pin the dictionary in the file header.
+
+Deliverables:
+
+1. **Statement — pinned to the chain modulus `𝓜(q,α)`, NOT Φ-generic**:
+ ```lean
+ structure RingSwitchStatement (innerRows messageDigits outerRows innerDigits dRows m r : Nat) where
+ pp : Hachi.PublicParamsD 𝓜(q,α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows
+ u : Commitment 𝓜(q,α) outerRows
+ xl : Vector (PackBase q α κRS) r
+ xh : Vector (PackBase q α κRS) m
+ xt : Vector (PackBase q α κRS) κ' -- κ' := α - κRS, the packed tail
+ y : PackBase q α κRS
+ ```
+ `q α κRS` enter as auto-bound section-variable parameters. Do NOT add a
+ `(Φ : CyclotomicModulus (ZMod q))` parameter: `PackBase` lives inside
+ `Rq (powTwoCyclotomic α)`, and nothing would tie a generic `Φ` to `α` — step 2 would then
+ fail to typecheck. The pin is sound because `𝓜(q,α)` is `@[reducible] hachiModulus q α :=
+ primePowTwoModulus q α` (InnerOuter/Arithmetic.lean:58) `:= powTwoCyclotomic α`
+ (CyclotomicRing/PowTwo.lean:58) — so `Rq 𝓜(q,α)` unfolds to `PackBase`'s ambient ring and
+ the coercions land without casts. (Statement stores the split point, matching
+ `PolyEvalStatement`'s discipline — verified docstring: split storage avoids take/drop casts.)
+2. **Output map** (fused adapter, G4):
+ ```lean
+ def toPolyEvalStatement (s : RingSwitchStatement …) (Y : Rq 𝓜(q,α)) :
+ PolyEvalStatement 𝓜(q,α) innerRows messageDigits outerRows innerDigits dRows m r :=
+ { pp := s.pp, u := s.u, xl := coeVec s.xl, xh := coeVec s.xh, y := Y }
+ ```
+ (`coeVec` from Phase 3 — the bare `(↑·)` lambda does not elaborate.)
+3. **pSpec, instances, check, verifier** (guarded, G3/G9). First bullet, all mandatory:
+ ```lean
+ @[reducible, simp]
+ def pSpecHead : ProtocolSpec 1 := ⟨!v[.P_to_V], !v[Rq 𝓜(q,α)]⟩
+
+ instance : IsEmpty (pSpecHead …).ChallengeIdx := ⟨fun ⟨0, h⟩ => nomatch h⟩
+ -- and, if not found via reducibility from ProtocolSpec/Basic.lean:295/:304:
+ instance : ∀ i, SampleableType ((pSpecHead …).Challenge i) := fun i => isEmptyElim i
+ ```
+ `@[reducible]` mirrors `SendClaim.pSpec` (SendClaim.lean:57) and is what lets the generic
+ 1-message instances (`IsEmpty ChallengeIdx`, `∀ i, SampleableType (Challenge …)` —
+ OracleReduction/ProtocolSpec/Basic.lean:295/:304) fire; a plain `def` stalls Phase 6's
+ package append on opaque instance-synthesis failures (`CWSSPackage.append` requires
+ `[∀ i, SampleableType (pSpec₁.Challenge i)]`, Package.lean:83). Then the Bool check (G9):
+ ```lean
+ noncomputable def headCheck (s : RingSwitchStatement …) (Y : Rq 𝓜(q,α)) : Bool :=
+ decide (s.y = ∑ v, (CMlPolynomial.monomialBasis s.xt).get v
+ * psiInv q α κRS h2 hk Y ((packIndexEquiv α κRS hκα).symm v))
+
+ noncomputable def headVerifier … :
+ Verifier oSpec (RingSwitchStatement …) (PolyEvalStatement …) (pSpecHead …) where
+ verify := fun s tr =>
+ if headCheck s (tr 0) then pure (toPolyEvalStatement s (tr 0)) else failure
+ ```
+ Notes: message access is plain `tr 0` (the SendWitness/SendClaim idiom — SendWitness.lean:73,
+ SendClaim.lean:107), not `tr.messages ⟨0, rfl⟩`. The `decide` needs
+ `DecidableEq (PackBase q α κRS)` — via `Subtype.instDecidableEq` from
+ `DecidableEq (Rq 𝓜(q,α))` (the chain's `[BEq]/[LawfulBEq]` context; check how QuadEval-side
+ files obtain `DecidableEq (Rq Φ)` and copy the route; if none exists, add the instance next
+ to `Rq`'s `commRing`). Bridge lemma for proofs: `headCheck s Y = true ↔ s.y = ∑ …` by
+ `decide_eq_true_eq`.
+4. **Input relation** (mirror `relPolyEval`'s three-case shape exactly):
+ ```lean
+ def relRingSwitch (base : ZMod q) (βSq γ κchal : ℕ) :
+ Set (RingSwitchStatement … × QuadEvalWitness 𝓜(q,α) innerRows (2^m) messageDigits (2^r) innerDigits) :=
+ { p | match p with
+ | (s, .opening o) =>
+ VerifiedOpening 𝓜(q,α) base βSq γ κchal s.pp.toPublicParams s.u o ∧
+ CMlPolynomial.eval (unpackPoly h2 hk (extractedPoly 𝓜(q,α) base o))
+ ((s.xl ++ s.xh) ++ s.xt) = s.y
+ | (s, .msisB z) => ModuleSIS.relation 𝓜(q,α) (outerShort 𝓜(q,α) γ) s.pp.outerMatrix z = true
+ | (s, .msisD z) => ModuleSIS.relation 𝓜(q,α) (dShort 𝓜(q,α) γ) s.pp.dMatrix z = true }
+ ```
+ Arity check: `extractedPoly … : CMlPolynomial (Rq 𝓜(q,α)) (r + m)`; `unpackPoly` gives
+ `CMlPolynomial (PackBase q α κRS) ((r + m) + κ')`; the point
+ `(s.xl ++ s.xh) ++ s.xt : Vector (PackBase q α κRS) ((r+m)+κ')`. ✓
+5. **Pull-back lemma** (the CWSS engine):
+ ```lean
+ theorem mem_relRingSwitch_of_relPolyEval (s) (Y) (w)
+ (hchk : headCheck s Y = true)
+ (h : (toPolyEvalStatement s Y, w) ∈ relPolyEval 𝓜(q,α) base βSq γ κchal) :
+ (s, w) ∈ relRingSwitch base βSq γ κchal
+ ```
+ Opening case: `relPolyEval` gives `CMlPolynomial.eval (extractedPoly …)
+ (coeVec s.xl ++ coeVec s.xh) = Y`; rewrite `coeVec xl ++ coeVec xh = coeVec (xl ++ xh)`
+ (a `Vector.map`/append commutation — verify the exact lemma name for the Vector type used by
+ `CMlPolynomial.eval`; if missing, prove it locally, 3 lines by `ext`/`get` — this is the only
+ Vector plumbing in the plan); cross `hchk` into the Prop form via `decide_eq_true_eq` (G9);
+ apply `unpackPoly_eval_of_check` (Phase 3.6). msisB/msisD: statement field `pp` passes
+ through unchanged — immediate.
+6. **Head CWSS theorem.** The head is a 1-message reduction: `IsEmpty ChallengeIdx` holds for
+ `pSpecHead` (step 3's instance). Verified proof route (the verification pass read
+ `treeSpecialSound`, TranscriptTree/Basic.lean:308-323, and both templates end-to-end):
+ ```lean
+ theorem head_coordinateWiseSpecialSound (init impl)
+ (D : CWSSStructure (pSpecHead …)) … :
+ (headVerifier …).coordinateWiseSpecialSound init impl D
+ (relRingSwitch base βSq γ κchal)
+ (relPolyEval 𝓜(q,α) base βSq γ κchal)
+ ```
+ — `D` **universally quantified** (both templates do this; the no-challenge bridge holds for
+ any `D`; a `(D := …)` default-value pseudo-binder is not valid syntax). Instantiate
+ `D := CWSSStructure.ofIsEmpty` only at the package (Phase 6). Proof skeleton, mirroring
+ `SendWitness.verifier_coordinateWiseSpecialSound` (SendWitness.lean:109) +
+ `ReduceClaim.verifier_coordinateWiseSpecialSound` (ReduceClaim.lean:186):
+ - Enter via `Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx` (its premise
+ imposes **no purity** on the verifier — verified — so the guard needs no Phase-5 machinery
+ here). The extractor `e : StmtIn → FullTranscript → WitIn` cannot see the output witness
+ (the transcript tree carries **no** `WitOut` at leaves — leaves are bare); recover it by
+ classical choice à la ReduceClaim's `hpick`: from acceptance,
+ `toPolyEvalStatement s (tr 0) ∈ relPolyEval.language`, and `Set.mem_language_iff` gives
+ `∃ w, … ∈ relPolyEval`; choose it (`Exists.choose`), here per `(s, tr)` since the chosen
+ witness depends on the message `tr 0`.
+ - (a) Acceptance forces the check: case on `headCheck s (tr 0)`. In the `false` branch
+ `verify = failure` and the run's acceptance probability is 0 ≠ 1 — add the missing helper
+ `not_accepting_of_failure` next to `pure_accepting_of_mem` (verified absent; the executable
+ spec for the probability argument is `CheckClaim.knowledgeStateFunction.toFun_full`'s
+ guard-false branch, via `probEvent_pos_iff`/support-of-`OptionT.mk (pure none)`).
+ - (b) In the `true` branch the run is `pure (toPolyEvalStatement s (tr 0))`: apply
+ `Verifier.mem_of_pure_accepting` (SeqCompose.lean:53) with `hV := if_pos hchk` (in place of
+ the templates' `rfl`), unpack via `Set.mem_language_iff`, and close with step 5.
+ - `[Nonempty WitIn]`: `instance : Nonempty (QuadEvalWitness …)` exists at QuadEval.lean:114
+ (verified) — same instance the bridge uses.
+7. **Prover skeleton.** Honest prover sends
+ `Y := CMlPolynomial.eval (extractedPoly …) (coeVec (s.xl ++ s.xh))`-style packed value.
+ Type (cf. `QuadEval.prover`, QuadEval.lean:323):
+ `Prover oSpec (RingSwitchStatement …) (QuadEvalWitness 𝓜(q,α) …)
+ (PolyEvalStatement 𝓜(q,α) …) (QuadEvalWitness 𝓜(q,α) …) (pSpecHead …)` — skeleton only
+ (G8). The head *package* (`GuardedCWSSPackage` value) is **deferred to Phase 6**, which is
+ where its structure type exists (Phase 5) and where the seam is checked.
+8. **Completeness-side lemma (statement only, G8):** `traceCheck_of_honest` — the honest `Y`
+ passes the check; provable from `psiInv_eval_coeVec` + the recombination identity; leave
+ proven if ≤ 1 day, else `sorry`-free *statement* deferred to the completeness TODO (do NOT
+ add a sorry — omit the lemma if unproven).
+
+*Why this phase:* this is the reduction itself. The guard (G3) is forced by information flow
+(`xt, y` dropped downstream); the direct-to-`PolyEvalStatement` output (G4) is what makes the
+`▷` seam close by `rfl`; the pull-back (step 5) is where Phase 3's algebra meets the chain's
+relation shapes.
+
+Acceptance: `head_coordinateWiseSpecialSound` sorry-free; file compiles inside the chain's
+namespace; `head_coordinateWiseSpecialSound`'s `relOut` argument is written **verbatim** as the
+§1.3 seam expression (`relPolyEval 𝓜(q,α) (b : ZMod q) (quadEvalBetaSq γ b zDigits
+((𝓜(q,α)).φ.natDegree) m messageDigits) γ (2 * ω)` at the chain instantiation) and elaborates
+without coercion. (The package-level `rfl`-seam example belongs to Phase 6 — the
+`GuardedCWSSPackage` type does not exist until Phase 5.)
+
+---
+
+## 8. Phase 5 — Guarded CWSS composition (2–3 d, framework; independent of Phases 1–4)
+
+Extend `CoordinateWiseSpecialSoundness/Composition.lean` + `Package.lean` (+ one helper in
+`TranscriptTree/Basic.lean`). **Coordinate with maintainers before landing** (shared security
+infrastructure — old plan R3; the Basic.lean TODO already sanctions the need).
+
+**Known tension to raise in that coordination:** CheckClaim.lean:26 and :185-189 record an
+unfinished "no-failure `OracleComp`" refactor under which guard-based verifiers are "retained as
+a rightmost-only factor" (the sanctioned workaround being: keep the verifier pure and move the
+check into the output relation, as `CheckClaim.oracleRelOut` does). That workaround is **not
+available** for the Hachi head: its check reads `s.xt, s.y`, which the output statement type
+drops (G3), so the check cannot live in `relOut`. Basic.lean's TODO ("Guarded subprotocols need
+a guarded variant of `▷`") is the sanctioned path; flag the refactor interaction explicitly.
+
+1. **`Verifier.IsGuarded`** (new, next to the pure machinery). Mirror `Verifier.IsPure`'s exact
+ style — it is a **class with an existential field** (OracleReduction/Basic.lean:748:
+ `is_pure : ∃ verify, ∀ …, V.verify … = pure …`), and `CWSSPackage.append` destructures it via
+ `obtain ⟨verify₁, hV₁⟩ := L₁.isPure.is_pure` (Package.lean:92); the guarded twin must
+ destructure the same way:
+ ```lean
+ /-- A verifier that either purely transforms the statement or rejects outright.
+ Pure verifiers are the `check := fun _ _ => true` case. -/
+ class Verifier.IsGuarded (V : Verifier oSpec StmtIn StmtOut pSpec) : Prop where
+ is_guarded : ∃ (check : StmtIn → pSpec.FullTranscript → Bool)
+ (out : StmtIn → pSpec.FullTranscript → StmtOut),
+ ∀ s tr, V.verify s tr = if check s tr then pure (out s tr) else failure
+ ```
+ (Bool check per G9 — matches Phase 4's `headVerifier` syntactically.)
+2. **Guarded run lemmas** (generalizing the verified anchors):
+ - `append_run_guarded_left` (from `append_run_pure_left`, Composition.lean:311): under the
+ guarded hypothesis, if `check s tr₁ = true` then
+ `(V₁.append V₂).run s (tr₁ ++ₜ tr₂) = V₂.run (out s tr₁) tr₂`; if `check s tr₁ = false`
+ then the composed run is `failure` (`failure >>= _ = failure` on `OptionT`).
+ - `not_accepting_of_failure` : if `V.verify s tr = failure` then
+ `Pr[(· ∈ lang) | …V.run…] = 0` — **verified missing**; add next to
+ `pure_accepting_of_mem` (:325-332). Executable spec for the probability argument:
+ `CheckClaim.knowledgeStateFunction.toFun_full`'s guard-false branch
+ (`probEvent_pos_iff` + support of `OptionT.mk (pure none)` contains no `some`).
+ - **`ChallengeTree.transcripts_ne_nil`** (new, `TranscriptTree/Basic.lean`, next to
+ `transcripts` at :178-183): `(∀ i, 0 < arity i) → ∀ {m} (T : ChallengeTree pSpec arity m)
+ pre, T.transcripts pre ≠ []` — structural induction, ~10 lines. *Why:* the guarded false
+ branch must exhibit SOME composed transcript to contradict acceptance; a suffix tree with a
+ zero-arity node lists no transcripts, making the shape-generic guarded theorem otherwise
+ unprovable (verified gap — no nonemptiness lemma exists anywhere in TranscriptTree/).
+3. **`Verifier.append_treeSpecialSound_of_guardedLeft`** — restate
+ `append_treeSpecialSound` (Composition.lean:366-375) with the guarded hypothesis replacing
+ the pure one, **plus the extra hypothesis `hS₂ : ∀ i, 0 < S₂.arity i`** (required for the
+ false branch, per the `transcripts_ne_nil` note above; the pure theorem needs no such
+ hypothesis, which is why this was invisible until now). Proof deltas: at the two `hV₁`
+ consumption sites (~:396, ~:407) case on `check s tr₁`: the `false` branch picks a suffix
+ transcript via `transcripts_ne_nil` + `hS₂` and contradicts composed acceptance
+ probability 1 via `not_accepting_of_failure` lifted along `append_run_guarded_left`; the
+ `true` branch reduces verbatim to the existing pure argument with `verify₁ := out`.
+ Corollary `append_coordinateWiseSpecialSound_of_guardedLeft` (mirror :414-428): discharge
+ `hS₂` from `D₂.arity_eq` — `arity i = ℓᵢ(kᵢ−1)+1 ≥ 1` by `coordIndex.2`/`soundnessParam.2`
+ (CWSSStructure fields carry `0 < ell` and `2 ≤ k`), so at the CWSS level the hypothesis is
+ free.
+ **3b. `Verifier.IsGuarded.append_isPureRight`** — `(hg : V₁.IsGuarded) (hp : V₂.IsPure) :
+ (V₁.append V₂).IsGuarded`, with `check := fun s tr => check₁ s tr.fst`,
+ `out := fun s tr => f₂ (out₁ s tr.fst) tr.snd` where `⟨f₂, hf₂⟩ := hp.is_pure`. Mirror
+ `Verifier.IsPure.append` (Composition/Sequential/IsPure.lean:37-43 — it supplies exactly the
+ `tr.fst`/`tr.snd` transcript split); `verify_eq` by `simp [Verifier.append, …]` + case-split
+ on the check. *Why:* Phase 6's composed package must certify its own `isGuarded` field; the
+ run lemmas of step 2 quantify over split transcripts and do not give this.
+4. **`GuardedCWSSPackage` + guarded `▷`** (Package.lean), fields spelled out:
+ ```lean
+ structure GuardedCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where
+ verifier : Verifier oSpec StmtIn StmtOut pSpec
+ struct : CWSSStructure pSpec
+ relIn : Set (StmtIn × WitIn)
+ relOut : Set (StmtOut × WitOut)
+ isGuarded : verifier.IsGuarded
+ isCWSS : verifier.coordinateWiseSpecialSound init impl struct relIn relOut
+
+ def GuardedCWSSPackage.append [∀ i, SampleableType (pSpec₁.Challenge i)]
+ (L₁ : GuardedCWSSPackage … pSpec₁) (L₂ : CWSSPackage … pSpec₂)
+ (hseam : L₁.relOut = L₂.relIn := by rfl) : GuardedCWSSPackage … (pSpec₁ ++ₚ pSpec₂) where
+ verifier := L₁.verifier.append L₂.verifier
+ struct := L₁.struct.append L₂.struct
+ relIn := L₁.relIn
+ relOut := L₂.relOut
+ isGuarded := L₁.isGuarded.append_isPureRight L₂.isPure
+ isCWSS := append_coordinateWiseSpecialSound_of_guardedLeft … -- + hseam rewrite
+
+ scoped infixr:65 " ▷! " => GuardedCWSSPackage.append -- name/notation: maintainer's call
+ ```
+ The `[∀ i, SampleableType (pSpec₁.Challenge i)]` binder mirrors `CWSSPackage.append`
+ (Package.lean:83) and is what Phase 4.3's pSpecHead instances exist to satisfy. (A pure
+ package lifts to a guarded one via `check := fun _ _ => true` — provide
+ `CWSSPackage.toGuarded` so mixed chains need only the one append.)
+5. Do **not** build the guarded n-ary `seqCompose` here (the old plan's B4 second half) — the
+ chain currently has exactly one guarded factor at the outer edge; the binary form suffices.
+ Record the n-ary variant in the Basic.lean TODO instead. *Reason:* smallest reviewable
+ framework change that unblocks Phase 6.
+
+*Why this phase:* verified fact — every CWSS composition theorem in the tree demands
+`V₁.verify stmt tr = pure (verify₁ stmt tr)` for the left factor; a rejecting head is therefore
+uncomposable today. The existing helper pair (`append_run_pure_left` / `pure_accepting_of_mem` /
+`mem_of_pure_accepting`) was verified to be exactly the right generalization surface.
+
+Acceptance: guarded append theorem sorry-free; existing `append_*` theorems byte-identical
+(`git diff` shows additions only); a toy `example` composing a trivially-guarded identity
+verifier with a pure one.
+
+---
+
+## 9. Phase 6 — Chain assembly (1 d)
+
+Host file (pinned): **new file `Hachi/RingSwitch/Chain.lean`**, importing `Hachi/Basic.lean` +
+`Hachi/RingSwitch/Head.lean` — this keeps Basic.lean's imports free of the RingSwitch subtree;
+Basic.lean receives only the diagram/TODO doc edits below.
+
+First define the head package here (deferred from Phase 4.7 — the structure type is Phase 5's):
+
+```lean
+def headPackage (init impl) (h2) (hk) (hκα) {b ω γ : ℕ} … :
+ GuardedCWSSPackage init impl (RingSwitchStatement …) (QuadEvalWitness …)
+ (PolyEvalStatement 𝓜(q,α) …) (QuadEvalWitness …) (pSpecHead …) where
+ verifier := headVerifier …
+ struct := CWSSStructure.ofIsEmpty
+ relIn := relRingSwitch (b : ZMod q)
+ (quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m messageDigits) γ (2 * ω)
+ relOut := relPolyEval 𝓜(q,α) (b : ZMod q)
+ (quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m messageDigits) γ (2 * ω)
+ isGuarded := ⟨headCheck …, toPolyEvalStatement …, fun _ _ => rfl⟩ -- shape per IsGuarded
+ isCWSS := head_coordinateWiseSpecialSound … CWSSStructure.ofIsEmpty …
+
+def ringSwitchChain (init impl) (hq5) (hκ) (hτ) (h2) (hk) (hκα) {b ω γ : ℕ} … :
+ GuardedCWSSPackage init impl
+ (RingSwitchStatement …) (QuadEvalWitness …)
+ (QuadEvalStatement … × CarrierCom … × (Fin (2^r) → ShortChallenge …))
+ (QuadEvalResponse …)
+ (pSpecHead ++ₚ ((!p[] : ProtocolSpec 0) ++ₚ pSpec …)) :=
+ headPackage … ▷! evalChain (b := b) (γ := γ) init impl hq5 hκ hτ
+
+theorem ringSwitch_eval_coordinateWiseSpecialSound … :
+ (…the composed verifier…).coordinateWiseSpecialSound init impl
+ (CWSSStructure.ofIsEmpty.append (CWSSStructure.ofIsEmpty.append (foldStructure …)))
+ (relRingSwitch (b : ZMod q) (quadEvalBetaSq …) γ (2 * ω))
+ (relOut (zDigits := zDigits) 𝓜(q,α) (b : ZMod q) ω γ) :=
+ (ringSwitchChain …).isCWSS
+```
+
+Instantiation discipline (verified §1.3): the head package is constructed at base
+`(b : ZMod q)`, `βSq := quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m messageDigits`,
+`κchal := 2 * ω` so the `▷!` autoparam seam closes by `rfl`. **Write the right factor as
+`evalChain (b := b) (γ := γ) init impl hq5 hκ hτ`** — `b, γ` are implicit in `evalChain`'s
+binders but absent from its result TYPE (verified), so leaving them to be solved through the
+autoparam's `rfl` goal is elaboration-order-fragile; instantiate them explicitly.
+
+Doc edits in `Hachi/Basic.lean` (same PR): in the header diagram (:51-74) mark the §3.1 head
+done AND **delete the now-obsolete "σ₋₁ statement adapter — planned (0-round ReduceClaim)" row**
+(:~58) — resolved by design, G2/G4: the head outputs `PolyEvalStatement` directly and the σ₋₁
+twist is discharged inside `traceCheck_iff_recombine`; point the head's arrow straight at the
+evalChain band. In the TODO block (:256-272): guarded-`▷` done, §3.1 head done, remaining items
+unchanged.
+
+*Why:* the deliverable theorem — subfield-point evaluation claim (paper §3.1 input) reduced to
+Eq. (20) + range checks with zero added soundness error, composed from sorry-free parts.
+
+Acceptance: theorem sorry-free; `./scripts/validate.sh` green; diagram/TODO updated in the same
+PR (CLAUDE.md guardrail).
+
+---
+
+## 10. Phase 7 — Carrier instance + hygiene (1 d)
+
+1. **`hachiCarrier`** (new `Hachi/RingSwitch/Carrier.lean`) — the corrected "S8 witness": a
+ genuinely non-domain, `P = E` carrier:
+ ```lean
+ noncomputable def hachiCarrier (h2) (hk) :
+ RingSwitchCarrier ↥(fixedSubring (R := ZMod q) α (2 ^ κRS)) where
+ P := Rq (powTwoCyclotomic (R := ZMod q) α)
+ E := ↥(fixedSubring (R := ZMod q) α (2 ^ κRS)) -- opening claims are base-valued
+ ιP := Fin (2 ^ α / 2 ^ κRS)
+ ιE := Unit
+ packBasis := Module.Basis.ofEquivFun (psiLinearEquiv q α κRS h2 hk).symm
+ openBasis := Module.Basis.singleton Unit _
+ ntP := nontrivial_Rq_powTwoCyclotomic … -- MUST be an explicit named field:
+ ntE := … -- Nontrivial (Rq …) is NOT a global instance
+ ```
+ **`ntP`/`ntE` must be assigned explicitly** — instance search cannot fill them (verified:
+ `Nontrivial (Rq …)` exists only as an inline `haveI` at Subfield/Field.lean:298-302, which
+ Phase 2.2 exports as `nontrivial_Rq_powTwoCyclotomic`; `ntE` = the subring nontriviality from
+ Phase 2.2). `ftP` (= `Fintype (Fin _)`) and `ftE` (= `Fintype Unit`) synthesize; `algP` via
+ Mathlib's `Algebra.ofSubring`; `algE` via `Algebra.id`. Note `Basis.ofEquivFun` wants
+ `M ≃ₗ[R] (ι → R)`, i.e. `psiLinearEquiv.symm` — then `packBasis.repr = psiLinearEquiv.symm`
+ definitionally (`ofEquivFun_repr_apply`, rfl), tying the carrier to Phase 3's `psiInv` by
+ `rfl`. Sanity `example`s: `hachiCarrier.packBasis.repr Y v = psiInv q α κRS h2 hk Y v := rfl`
+ (this one IS rfl); `recombineCheck (hachiCarrier …) w Y y ↔ (Phase 4's check)` — **not rfl**:
+ the sums range over `Fin (2^α/2^κRS)` vs `Fin (2^(α−κRS))` (not defeq, G7); prove via
+ `Fintype.sum_equiv (packIndexEquiv α κRS hκα)` with
+ `w := (CMlPolynomial.monomialBasis xt).get ∘ packIndexEquiv α κRS hκα`.
+ *Why:* pins the generic-layer correspondence, gives the branch its promised non-domain carrier
+ with the **correct** (packing-stage) role, and keeps `Generic/PackedClaim.lean`'s lemmas
+ honest against a real instance. Not on the head's proof-critical path — if instance plumbing
+ fights (`Nontrivial (Rq …)` derivation), timebox to half a day and land the carrier with the
+ Nontrivial argument as an explicit hypothesis-parameter instead.
+2. Wiki/KB: `repo-map.md` (Generic/PackedClaim, Hachi/RingSwitch/), KB ring-switching page
+ (packing-stage + corrected S8 + the `hachiCarrier` pointer), `docs/kb/papers/NOZ26.md` gap
+ list if present.
+3. Update `HACHI_RING_SWITCHING_PLAN.md`: mark B1–B3 superseded by `Generic/PackedClaim.lean`,
+ B4 superseded by Phase 5 (binary case; n-ary still open), C1–C3 + D1–D2 superseded by Phases
+ 2–4 + 6; note R2 and D5's twist are resolved-by-design (G2); Phases A, E–G unaffected
+ (Phase E's derive-`y₀` head and Phases F/G continue to apply downstream of this plan's head).
+4. `./scripts/validate.sh --lint` (and `--docs` if docstrings were the day's work).
+
+---
+
+## 11. Standing hypotheses & conventions (pin before writing any Lean)
+
+- `q` prime, `[Fact (Nat.Prime q)] [NeZero q] [BEq (ZMod q)] [LawfulBEq (ZMod q)]`; `q` explicit
+ in lattice-layer lemmas (house style). `h2 : (2 : ZMod q) ≠ 0` threaded explicitly (derivable
+ from `q % 8 = 5` at the chain level if desired — small lemma, optional).
+- `hk : 2 * 2 ^ κRS ∣ 2 ^ α` everywhere ψ appears; `hκα : κRS ≤ α` via
+ `succ_le_of_two_mul_two_pow_dvd`.
+- **`κRS` (packing) vs `κ`/`κchal` (chain challenge param, instantiated `2 * ω`) vs paper-`κ`**:
+ three different things; the file headers of Head.lean/Unpack.lean must carry the dictionary.
+- Index forms: `2 ^ α / 2 ^ κRS` in all ψ-adjacent types; `2 ^ (α − κRS)` only after
+ `packIndexEquiv`; **no other casts**.
+- Variable order: unpacked polynomial has the packed tail **last** (`(xl ++ xh) ++ xt`), matching
+ the paper and `relPolyEval`'s `xl ++ xh`; the branch's `curryFamily` (prefix packing) is NOT
+ used by the Hachi path — do not import its convention.
+- All new Hachi-side defs `noncomputable` where ψ/traceH forces it; the guard check is
+ Bool-valued per **G9** (`headCheck := decide (…)`; no un-scoped `Classical`); coercions
+ `B → Rq` go through `coeVec` (Phase 3 — bare `(↑·)` does not elaborate).
+- New files must be `git add`ed before `./scripts/update-lib.sh` / validation (generated
+ `ArkLib.lean`).
+
+## 12. What NOT to do (each has bitten before)
+
+- Do not hand-edit `ArkLib.lean` (generated; `scripts/update-lib.sh`).
+- Do not `open ArkLib.Lattices` in chain files (ambiguous `⬝ᵥ` — verified docstring warning).
+ The ban is on that namespace **itself**; opening the leaf namespaces
+ `ArkLib.Lattices.CyclotomicModulus` (needed for psi/traceH/fixedSubring — Basic.lean:97
+ already does it) and `ArkLib.Lattices.Hachi` is safe and required.
+- Do not resolve `Fin (2^α/2^κ)` vs `Fin (2^(α−κ))` mid-proof — go through `packIndexEquiv`.
+- Do not route the Hachi head through `BatchingStrategy`, `DenseMLPCS`, or the MvPolynomial
+ generic lemmas (G1/G5) — the RBR obligations are unfilled and there is no CWSS bridge.
+- Do not add `sorry` inside structures or instances; laws are theorem hypotheses (G6).
+- Do not modify `openingClaimRel`/`sliceRel`/`sumcheckClaimRel`/`BatchingStrategy` definitions —
+ Phase 1 is additive plus docstrings only (the branch's Binius path must stay intact).
+- Do not build the CWSS work on `origin/cwss-components-infra` — it *lacks* `ofIsEmpty`,
+ `SingleRound.lean`, `Package.lean` (verified); the working tree is the source of truth.
+- Do not conflate `Hachi.toPolynomial` (matrix reshape) with CompPoly's
+ `CMlPolynomial.toMvPolynomial` (no eval lemma).
+
+## 13. Risk register
+
+| # | Risk | Mitigation |
+|---|---|---|
+| R1 | `treeSpecialSound`'s output-witness flow differs from what Phase 4.6 assumes | **Resolved by verification**: the direct route is viable — the no-challenge bridge (NoChallenge.lean:118) imposes no purity, so the guarded 1-message head is provable by mirroring SendWitness (pSpec shape, `tr 0`) + ReduceClaim (`hpick`/`Exists.choose` witOut recovery from `Set.mem_language_iff`) + the new `not_accepting_of_failure` helper (executable spec: `CheckClaim.knowledgeStateFunction.toFun_full`). The 2-factor-decomposition fallback is legal only with the guarded factor as the RIGHT append factor, still requires Phase 5 for the outer composition, and reuses no existing CWSS theorem — prefer the direct route. |
+| R2 | `PolyMatrix` orientation in `unpackPoly` transposed | Acceptance test is `unpackPoly_eval` itself; fix at the definition (G7), plus the Phase 3 toy example. |
+| R3 | Framework changes (Phase 5) touch shared files | Coordinate with maintainers; additions only; existing theorems byte-identical (acceptance-checked). |
+| R4 | `Vector.map_append` or similar plumbing missing in the Vector API used by CompPoly | Prove locally (3-line `ext`); do not refactor the Vector library. |
+| R5 | `DecidableEq (Rq …)` not available where Phase 4.3's `decide` needs it | Derive from the chain's `[LawfulBEq (ZMod q)]` context (e.g. `instDecidableEqOfLawfulBEq`-style, or add the instance next to `Rq`'s `commRing`); last resort: `headCheck` via `==` (`BEq`) with a `LawfulBEq` bridge lemma into the Prop form used by `relRingSwitch` (still G9-conformant — the check stays Bool). |
+| R6 | Merge conflicts from the dirty working tree at Phase 0 | Commit first (hard requirement in Phase 0.1). |
+| R7 | `Nontrivial (Rq …)` instance derivation fights (Phase 7) | Timeboxed; hypothesis-parameter fallback specified. |
+| R8 | Branch author's in-flight S6 work collides with Phase 1 | Phase 1 is additive + docstrings; raise the PackedClaim stage with the author before merging (it slots as a new stage between S2 and S5 in their numbering). |
diff --git a/HACHI_LEMMA10_GAP.md b/HACHI_LEMMA10_GAP.md
new file mode 100644
index 0000000000..67b49d6686
--- /dev/null
+++ b/HACHI_LEMMA10_GAP.md
@@ -0,0 +1,852 @@
+# The Lemma 10 Gap in Hachi — Analysis and Repair
+
+Companion to [`HACHI_RING_SWITCHING_PLAN.md`](HACHI_RING_SWITCHING_PLAN.md) (milestone F6, risk
+R7) and [`HACHI_RING_SWITCHING_COMPARISON.md`](HACHI_RING_SWITCHING_COMPARISON.md). Subject:
+**Lemma 10 of Hachi (NOZ26, ePrint 2026/156, §4.3, Fig. 5)** — the coordinate-wise special
+soundness (CWSS) claim for the zero-check challenge round — is not provable as stated, and the
+gap is witnessed by an explicit, protocol-level counterexample against the paper's own
+range-check polynomial. This file states the gap precisely, shows which repairs work and which
+do not, and records the rendering adopted by the formalization plan.
+
+**TL;DR.** A star-shaped family of accepting transcripts certifies that a batched
+constraint polynomial vanishes on the *axis cross* through the star's center — and for a
+multilinear polynomial in at least two challenge variables, cross-vanishing does **not** imply
+that the polynomial is zero. An adversary can commit to a witness with a single out-of-range
+entry and present a perfectly valid, correctly structured one-round star from which no
+extractor can succeed without breaking binding. No choice of the paper's parameter `D` helps.
+The protocol itself is very likely still sound for uniformly random challenges, but the stated
+deterministic tree-extraction claim is false.
+
+The best repair is still **one round**. Restrict each random evaluation point to the Kronecker
+curve
+
+```
+κ_m(ρ) := (ρ, ρ², ρ⁴, …, ρ^(2^(m-1))).
+```
+
+For an `m`-variate multilinear `H`, the pullback `H(κ_m(T))` is univariate of degree less than
+`2^m`, and the pullback is injective: distinct multilinear monomials become the distinct powers
+`T^0,…,T^(2^m−1)`. Sample independent scalar seeds `(ρ_0,ρ_α) ∈ F²` in one verifier round and
+send `τ_0 := κ_{m_0}(ρ_0)` and `τ_α := κ_{m_α}(ρ_α)`. With
+
+```
+D := max(2^m_0, 2^m_α),
+```
+
+an `SS(F,2,D)` star gives `D` roots of each pullback on its corresponding arm, hence both
+original identities. Its tree has `2D−1` leaves. If the two checks share one seed `ρ`, the same
+argument gives ordinary `D`-special soundness with only `D` leaves. The existing equality-kernel
+sumchecks remain unchanged because they simply receive the structured points `κ_m(ρ)`. The
+tradeoff is that the evaluation points are curve-distributed rather than uniform in `F^m`, and
+the error scale becomes `D/|F|` rather than `m/|F|`; one must assume `D ≤ |F|` and choose the
+extension field accordingly. The coordinate-zipped construction below remains a sound fallback
+when retaining the original uniform challenge distribution is more important than one-roundness.
+
+Throughout, `F := F_{q^k}` is the challenge field; `m_0` and `m_α` (also written `m_1`) are the
+arities of `H_0` and `H_α`; `N_0 := 2^m_0`, `N_α := 2^m_α`, and
+`D := max(N_0,N_α)`; and "multilinear" means
+degree at most one in each challenge variable. The paper calls the second point `τ_1`; this file
+also writes `τ_α` to make its role unambiguous. In the zipped fallback, `r := max(m_0,m_α)` and
+`s := min(m_0,m_α)`.
+
+## 1. Faithful setting
+
+Hachi §4.3 must prove, for a committed witness `w̃` (Eq. (21): the `Z_q`-coefficient table of
+the Eq. (20) solution `(ŵ, t̂, ẑ)` and the quotient digits, indexed by `(u, ℓ)`), that
+
+- the lifted linear rows hold at the challenge `α` (already reduced by Fig. 4 / Lemma 9), and
+- every entry of `w̃` is in range: `w̃(u,ℓ) · ∏_{j=1}^{b−1} (w̃(u,ℓ) − j)(w̃(u,ℓ) + j) = 0`.
+
+Both constraint families are batched with the equality kernel (Eqs. (22), (23)):
+
+```
+H_α(t) := Σ_{i ∈ [n]} eq̃(t, i) · ( Σ_{u,ℓ} M̃_α(i,u) · w̃(u,ℓ) · α̃(ℓ) − y_i(α) )
+H_0(t) := Σ_{u,ℓ} eq̃(t, (u,ℓ)) · w̃(u,ℓ) · ∏_{j=1}^{b−1} (w̃(u,ℓ) − j)(w̃(u,ℓ) + j)
+```
+
+Both are **multilinear in `t`** (only `eq̃` depends on `t`, and `eq̃(·, i)` is multilinear).
+Fig. 5 has the verifier send `τ_0` (for `H_0`) and `τ_1` (for `H_α`) in one round; the claims
+`H_0(τ_0) = 0` and `H_α(τ_1) = 0` then seed the sumcheck (Figs. 6–7).
+
+**Lemma 10 (paper, condensed).** Given `D := max(2d, 2b−1)` valid transcripts
+`((τ_{i,0}, τ_{i,1}), w̃_i)` with `(τ_{i,0}, τ_{i,1})_i ∈ SS(F_{q^k}, 2, D)`, one can either
+extract a valid opening `w̃` of `t` satisfying `H_0 ≡ 0` and `H_α ≡ 0`, or break binding of
+`Com`. *Proof (paper):* if two `w̃_i` differ, binding breaks; otherwise "by definition of
+`D = max(2d, 2b−1)` and the coordinate-wise special soundness, we have found at least `2d`
+(resp. `2b−1`) distinct roots for `H_α` (`H_0`), which implies that the aforementioned
+polynomials are equal to zero."
+
+The statement is ambiguous about the CWSS shape — the lemma writes `SS(F, 2, D)` (which types
+the challenge as a vector of `ℓ = 2` field elements, impossible for
+`(τ_0, τ_1) ∈ F^{m_0} × F^{m_1}`), while the surrounding text says to treat `(τ_0, τ_1)` "as a
+vector of `log μ + log d + log n` coordinates" (`ℓ = m := m_0 + m_1`; note this count is itself
+inconsistent with the paper's own Eq. (21)/(23) index space `[μ+n] × [d]` — see plan F5 — and
+the transcript count "`D` valid transcripts" disagrees with the `SS(S, ℓ, k)` set size
+`K = ℓ(k−1)+1` under either reading: `2D−1` for `ℓ = 2`, `m(D−1)+1` coordinate-wise). §2
+refutes the coordinate-wise reading (the substantive one); §3.0 disposes of the literal
+`ℓ = 2` reading.
+Recall the CWSS/`SS(S, ℓ, k)` shape ([FMN24] Def. 2.9, NOZ26 §2.3, formalized as
+`CoordinateWise.IsSpecialSoundFamily`,
+[Basic.lean:81](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Basic.lean#L81)):
+a **star** — one central vector `c`, plus `k−1` siblings per coordinate `i`, each agreeing with
+`c` everywhere except at coordinate `i`.
+
+## 2. The gap
+
+### 2.1 What a star actually certifies
+
+**Lemma A (star ⇒ cross, and nothing more).** Let `H ∈ F[t_1, …, t_m]` be multilinear and let a
+family of points vanish `H`, consisting of a center `c ∈ F^m` and, for each coordinate `j`, at
+least one point `c^{(j)}` with `c^{(j)}_i = c_i` for `i ≠ j` and `c^{(j)}_j ≠ c_j`. Then `H`
+vanishes identically on every axis line `L_j := { p : p_i = c_i for all i ≠ j }`.
+
+*Proof.* The restriction of `H` to `L_j` is a univariate polynomial of degree ≤ 1 in `t_j`
+(multilinearity). It vanishes at the two distinct points `c_j` and `c^{(j)}_j`, hence is the
+zero polynomial. ∎
+
+So *any* number `k ≥ 2` of siblings per coordinate certifies exactly: `H` vanishes on the
+**axis cross** `⋃_j L_j` through `c`. The question is whether cross-vanishing forces `H ≡ 0`.
+For `m = 1` it does (the cross is the whole space). For `m ≥ 2` it does not:
+
+**Counterexample B (polynomial level).** Let `m ≥ 2`, `a, b ∈ F`, and
+`H*(t) := (t_1 − a)(t_2 − b)`. `H*` is multilinear and `H* ≢ 0` (it is `1` at
+`(a+1, b+1, …)`), yet `H*` vanishes on the entire cross through any center `c` with `c_1 = a`,
+`c_2 = b`: every point of every axis line through `c` retains `t_1 = a` or `t_2 = b`. Moreover,
+since `{eq̃(·, i)}_{i ∈ {0,1}^m}` is a basis of the multilinear polynomials (evaluation at the
+Boolean points is the identity matrix: `eq̃(i', i) = δ_{i,i'}`), we have
+`H* = Σ_i eq̃(t, i) · H*(i)` — so `H*` is *exactly of the batched form* in Eqs. (22)/(23), with
+coefficient vector `c_i := H*(i)` not all zero. ∎
+
+Lemma A + Counterexample B already refute the paper's proof *step* ("roots ⇒ zero"). The
+following upgrades this to a refutation of the lemma's *statement*, i.e. an attack an adversary
+can actually mount inside the protocol.
+
+### 2.2 A constructive, protocol-level counterexample (via the range check)
+
+The range-check polynomial `H_0` is the easiest to weaponize because the adversary controls its
+coefficients **entrywise**: the coefficient of `eq̃(t, (u,ℓ))` is
+`P_b(w̃(u,ℓ))` where `P_b(v) := v·∏_{j=1}^{b−1}(v−j)(v+j)`, a fixed nonzero polynomial of
+degree `2b−1`.
+
+**Construction.** Assume `q > 2b−1` (true for all real parameters; Fig. 9 has `q ≈ 2^32`,
+`b = 16`) and `m_0 ≥ 2` (always: `m_0` is the log of the witness table size). Note the entries
+of `w̃` live in `Z_q` (Eq. (21): they are `X`-coefficients of `R_q`-elements), so the
+out-of-range value must be chosen in `Z_q`, and — because the paper instantiates *weak* binding
+(Remark 2 / Lemma 7), whose second-opening consequence is norm-conditioned — it must also be
+**small**, so that the eventual opening pair yields a genuinely short MSIS solution.
+
+1. Set `v* := b ∈ Z_q`. Then `P_b(v*) = b·∏_{j=1}^{b−1}(b−j)(b+j)`: every factor lies in
+ `{1, …, 2b−1}`, hence is nonzero mod `q` by `q > 2b−1`, so `P_b(v*) ≠ 0`. Pick any index
+ `(u*, ℓ*)` covered by the range check (all of them under Eq. (23); take `u* ≤ μ` if the
+ `1_{≤μ}` convention of `F_{0,τ_0}` is used).
+2. Build `w̃` as the **all-zero table except** entry `(u*, ℓ*) := v*` (all other entries must be
+ in range, else `H_0` acquires further `eq̃`-monomials and step 4's cross argument breaks;
+ zero is in range). Then choose the *statement* to match the linear part — take the public
+ `y`-side to be whatever the lifted rows evaluate to on this `w̃`, with the honest quotient
+ (Lemma 10 is a statement about arbitrary public inputs; the adversary who controls the
+ earlier protocol messages controls the R^lin statement it feeds). Then `H_α ≡ 0`
+ identically, and `H_0(t) = P_b(v*) · eq̃(t, (u*, ℓ*)) ≢ 0`.
+3. Commit honestly: `t := Com(w̃)`. Note the zero set of `H_0` is the union of hyperplanes
+ `⋃_j { t_j = 1 − i*_j }` where `i* ∈ {0,1}^{m_0}` is the bit pattern of `(u*, ℓ*)`.
+4. Choose the star center `c ∈ F^{m_0}` with `c_{j_1} = 1 − i*_{j_1}` and
+ `c_{j_2} = 1 − i*_{j_2}` for two distinct coordinates `j_1 ≠ j_2` (arbitrary elsewhere).
+ Every axis line through `c` fixes all-but-one coordinate, so it retains at least one of the
+ two vanishing coordinates — the entire cross lies in the zero set of `H_0`. Populate the
+ star with any `D−1` siblings per coordinate; choose `τ_1`-parts arbitrarily.
+5. Every transcript in the family is **valid**: `t = Com(w̃)` holds, `H_α(τ_1) = 0` holds
+ identically, and `H_0(τ_0) = 0` holds at every star point by step 4.
+
+All transcripts carry the *same* `w̃`, so the extractor's binding branch is unavailable, and its
+main branch must output an opening of `t` whose entries are all in range (`H_0 ≡ 0` forces every
+entry to be a root of `P_b`, i.e. in `[−(b−1), b−1]`) — any such opening differs from `w̃`
+(whose entry `(u*,ℓ*) = b` is out of range), and the difference is entrywise bounded by
+`2b−1` — **short**. The pair is therefore exactly a *weak-binding* break in the paper's sense
+(a short Module-SIS solution via Lemma 7): the extractor itself would be an efficient
+weak-binding breaker. Under the binding assumption, no efficient extractor exists. Lemma 10,
+under the coordinate-wise reading, is false — for *every* value of `D`. ∎
+
+Two remarks. (i) The attack lands on the **range check** — the exact-norm-proof feature that is
+the paper's headline contribution — so this is not a peripheral technicality. (ii) The attack
+does not need `H_α`: it is orthogonal to the Lemma 9 layer, whose own soundness (univariate,
+`k = 2d`) is fine.
+
+### 2.3 Diagnostics: where the paper's proof goes wrong
+
+- **Degree confusion.** `H_α` and `H_0` are multilinear *in `t`*. The quantities `2d` and
+ `2b−1` are degrees in *other* variables — `2d−1` bounds the `X`-degree of the lifted rows
+ (that is Lemma 9's interpolation, over the `α`-challenge), and `2b−1` is the `w̃`-degree of
+ the range product (Lemma 11's per-round sumcheck degree is then `2b` — range product times
+ the multilinear `eq̃` — with `k = 2b+1` transcripts per round; plan F5/R8). Neither has
+ anything to do
+ with identity-testing the `t`-polynomials; "`2d` (resp. `2b−1`) distinct roots" for a
+ *multivariate multilinear* polynomial implies nothing. Even along a single coordinate line,
+ 2 points already suffice — and all `m` lines together still do not determine `H` (Lemma A +
+ Counterexample B). So `D = max(2d, 2b−1)` is simultaneously wasteful (per line) and
+ insufficient (globally).
+- **Why the paper's *other* CWSS lemmas are unaffected.** Lemma 8 (QuadEval/folding, formalized
+ sorry-free) uses stars *correctly*: its verification equations are affine in each challenge
+ coordinate, and extraction subtracts the center transcript from a sibling to *isolate one
+ column* — the star is exactly the right shape for folding, and no "vanishing ⇒ zero
+ polynomial" step occurs. Lemmas 9 and 11 are single-scalar-challenge rounds — univariate
+ interpolation, rigorous. The misuse is specific to Lemma 10's *multivariate zero-check*.
+- **What survives.** The *protocol* is almost certainly still knowledge-sound: for uniformly
+ random `τ`, a nonzero multilinear `H` in `m` variables vanishes with probability at most
+ `m/|F|` (Schwartz–Zippel; total degree ≤ m), and the adversarial cross is a
+ measure-`O(m/|F|)` event. What is broken is the *tree-extraction claim* — precisely the
+ currency in which the paper (via FMN24 Lemma 4) and the ArkLib formalization (via
+ `CWSSStructure` composition) do all their accounting.
+
+## 3. Repair approaches
+
+Summary table; details below.
+
+| # | Approach | Sound? | Verdict |
+|---|---|---|---|
+| K | **One-round Kronecker-curve challenges, `k = D`** | ✓ | **adopted** (plan F6) |
+| 0 | Literal `ℓ = 2` reading, with unrestricted uniform vector blocks | ✗ | fails |
+| 1 | Coordinate-zipped sequential CWSS rounds, `k_j = 2` | ✓ | uniform-challenge fallback |
+| 2 | Fully sequential scalar rounds, `k_j = 2` | ✓ | sound fallback, but dominated by 1 |
+| 3 | One round with a tensor-grid tree shape | ✓ | sound, but needs a new non-CWSS tree predicate |
+| 4 | Keep the original one-round star and appeal only to the separation of `H_0` and `H_α` | ✗ | separation helps only after re-scheduling |
+| 5 | Keep the original vector-coordinate star and increase `k`/`D` | ✗ | no parameter helps without re-encoding |
+| 6 | Rewinding plus Schwartz–Zippel for the original uniform vectors | ✓* | sound, but not deterministic CWSS extraction |
+| 7 | Direct scalar power fingerprint `Σ_i c_i T^i` | ✓ | one-round alternative; changes the multiplier |
+
+### 3.K One-round Kronecker-curve CWSS — works (adopted)
+
+The missing ingredient is not another tree shape; it is a challenge encoding on which ordinary
+univariate interpolation is information-complete for multilinear polynomials.
+
+#### 3.K.1 The injective pullback
+
+For `m ≥ 1`, define the Kronecker curve
+
+```
+κ_m : F → F^m,
+κ_m(ρ)_j := ρ^(2^j) for j = 0,…,m−1.
+```
+
+Write an arbitrary multilinear polynomial in the monomial basis:
+
+```
+H(X_0,…,X_{m-1}) = Σ_{e ∈ {0,1}^m} a_e · ∏_j X_j^(e_j).
+```
+
+Its pullback is
+
+```
+K_H(T) := H(κ_m(T))
+ = Σ_{e ∈ {0,1}^m} a_e · T^(Σ_j e_j 2^j).
+```
+
+The binary encoding `e ↦ Σ_j e_j 2^j` is a bijection from `{0,1}^m` to
+`{0,…,2^m−1}`. Consequently,
+
+```
+deg K_H < 2^m,
+K_H = 0 ⇔ H = 0. (Kronecker injectivity)
+```
+
+This is stronger than a Schwartz–Zippel statement: it is a deterministic polynomial identity
+equivalence. ArkLib already has the forward map as
+`LinearMvExtension.powAlgHom` in
+[`LinearMvExtension.lean`](ArkLib/Data/MvPolynomial/LinearMvExtension.lean); its existing
+`powAlgHom_of_restrict_degree_natDegree` proves the degree bound. The main generic algebra lemma
+still needed for this repair is injectivity of `powAlgHom` on the per-variable-degree-`≤1`
+subtype. The file's inverse construction `linearMvExtension` already contains almost all of that
+proof.
+
+#### 3.K.2 Protocol rendering
+
+Let
+
+```
+N_0 := 2^m_0,
+N_α := 2^m_α,
+D := max(N_0,N_α),
+```
+
+and assume `D ≤ |F|`. Replace Fig. 5's unrestricted vector sampling by the following single
+public-coin round:
+
+```
+ρ_0, ρ_α ← F independently
+τ_0 := κ_{m_0}(ρ_0)
+τ_α := κ_{m_α}(ρ_α)
+send (τ_0,τ_α) -- or send the two seeds and derive the vectors
+```
+
+The prover response and verifier equations stay exactly as in Fig. 5:
+
+```
+t = Com(w̃),
+H_0^{w̃}(τ_0) = 0,
+H_α^{w̃}(τ_α) = 0.
+```
+
+The protocol's challenge type should be modeled as `F²`, with the two vectors derived
+deterministically. If the expanded vectors themselves are placed in the semantic transcript,
+their types must be the **curve-image subtypes**, not unrestricted `F^m`: injectivity of `κ_m`
+(its first coordinate is `ρ`) then gives the required equivalence between each subtype and `F`.
+Merely serializing the expanded vectors on the wire is harmless, but ArkLib's `Challenge` type
+must retain the on-curve invariant so that every family admitted by the `CWSSStructure` consists
+of curve points.
+
+This Lemma 10 block is at a **fixed, previously extracted `α`**, exactly as in the paper's lemma
+statement `(t,M̃_α,α)`. The `α` fork from Lemma 9 must remain an earlier/nested extraction node.
+Even if an implementation coalesces `α,ρ_0,ρ_α` into one byte message, treating all three as one
+flat CWSS star is not justified: the `H_α` check has mixed dependence on `α` and `ρ_α`, recreating
+the same missing-corners problem.
+
+#### 3.K.3 Extraction from one CWSS star
+
+An `SS(F,2,D)` family has not `D` but
+
+```
+2(D−1)+1 = 2D−1
+```
+
+members. Relabel it around its center as
+
+```
+(a,b),
+(a_1,b), …, (a_{D-1},b),
+(a,b_1), …, (a,b_{D-1}),
+```
+
+where `a,a_1,…,a_{D-1}` are distinct and so are `b,b_1,…,b_{D-1}`.
+
+If two accepting branches return different admissible openings of `t`, return the same
+binding/weak-binding escape as in the paper. Otherwise binding fixes one common `w̃`. The first
+arm now gives `D` distinct roots of
+
+```
+K_0(T) := H_0^{w̃}(κ_{m_0}(T)), deg K_0 < N_0 ≤ D,
+```
+
+so `K_0 = 0`, and Kronecker injectivity gives `H_0^{w̃} = 0`. The second arm identically gives
+`H_α^{w̃} = 0`. The bad axis-cross polynomial from §2 cannot survive this challenge encoding:
+its pullback is a nonzero univariate polynomial of degree less than `N_0`, hence it cannot vanish
+at all `D` first-arm seeds.
+
+This yields the corrected statement:
+
+**Lemma 10 (corrected: one-round Kronecker CWSS).** Suppose `m_0,m_α ≥ 1`,
+`D := max(2^m_0,2^m_α) ≤ |F|`, and the Fig. 5 points are derived from independent scalar seeds
+by `κ`. There is an efficient deterministic extractor which, from a family of `2D−1` accepting
+transcripts whose seed pairs lie in `SS(F,2,D)`, returns either
+
+1. one opening `w̃` of `t` satisfying `H_0^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0`, or
+2. the commitment binding escape used by the surrounding Hachi proof.
+
+Thus the modified zero-check is one-round `(2,D)`-coordinate-wise special sound. The extractor
+is polynomial time whenever the checked table sizes `N_0,N_α` are polynomial in the security
+parameter, exactly the regime required for the protocol itself to be efficient. For a zero-arity
+identity, test the resulting constant directly and take `D := max(2,N_0,N_α)` to meet ArkLib's
+nontrivial-parameter convention.
+
+#### 3.K.4 Plain-special-soundness variant
+
+If independence between the two batching points is unnecessary, sample one `ρ ← F` and set
+
+```
+τ_0 := κ_{m_0}(ρ),
+τ_α := κ_{m_α}(ρ).
+```
+
+Then any `D` accepting transcripts with distinct `ρ` give `D` roots of *both* pullbacks. The
+protocol is ordinary `D`-special sound (`ℓ=1`) and its extraction input has only `D` leaves. This
+is the smallest one-round rendering, but the independent-seed CWSS variant is closer to Fig. 5
+and lets the two tests retain cross-block independence.
+
+The independent-seed protocol is not ordinary `D`-special sound merely by treating a pair as
+one challenge: `D` distinct pairs need not contain `D` distinct first coordinates or `D`
+distinct second coordinates. Its two star arms are exactly what the CWSS hypothesis supplies.
+
+#### 3.K.5 Cost and faithfulness
+
+- **Rounds and payload.** There is one challenge round. Sending seeds costs two field elements;
+ sending the derived vectors retains Fig. 5's message shape but spends `m_0+m_α` elements.
+- **Downstream sumchecks.** They are unchanged: substitute the derived `τ_s=κ_{m_s}(ρ_s)` into
+ the same `eq̃(τ_s,·)` multiplier. Repeated squaring computes each curve point in `O(m_s)` field
+ operations.
+- **Tree size.** Independent seeds use `2D−1` leaves; a shared seed uses `D`. When
+ `N_0=N_α=N`, the zipped fallback uses `3^{log₂ N}=N^{log₂ 3}` leaves, so both curve variants
+ asymptotically improve the extraction tree.
+- **Error tradeoff.** A false fixed opening makes at least one nonzero pullback of degree at most
+ `D−1`, so its relevant uniform seed lands on a root with probability at most
+ `(D−1)/|F|`. This is worse than the `O((m_0+m_α)/|F|)` scale of uniform vector evaluation.
+ Asymptotically `D=poly(λ)` and `|F|=2^{Ω(λ)}` still give negligible error. Concretely, the
+ paper's largest next-witness table has size about `2^26` over a field of size about `2^128`,
+ so a single curve test supplies only about 102 bits from this term; a 128-bit target needs a
+ larger extension field or parallel repetition. Repeated seeds can still be sent in the same
+ verifier message, although the corresponding CWSS coordinate count and star arity grow.
+ In particular, do not apply the `2D/|F|²`-shaped expression printed in Hachi's restatement of
+ FMN24 literally here: if only `H_0` is invalid while `H_α=0`, acceptance can already have
+ probability `(N_0−1)/|F|`. The direct root bound is the safe accounting.
+- **Faithfulness.** The verifier no longer samples uniformly from all of `F^{m_0}×F^{m_α}`.
+ It samples uniformly from two size-`|F|` Kronecker curves. This is a genuine, localized
+ protocol change, though the checked equations and every downstream sumcheck formula are the
+ same.
+- **Field-size condition.** `D ≤ |F|` is load-bearing: without it an `SS(F,2,D)` family cannot
+ exist, and the root argument cannot collect `D` distinct seeds.
+- **Scalar-threshold optimality.** For `N` arbitrary residual coefficients, any linear
+ one-scalar batching defines a length-`|F|`, dimension-`N` evaluation code. If `k` accepting
+ roots always force the coefficient vector to vanish, its distance is at least `|F|−k+1`;
+ the Singleton bound gives distance at most `|F|−N+1`, hence `k ≥ N`. Kronecker and direct
+ powers attain this bound. The table-sized threshold is therefore inherent in this generic
+ scalar-linear model, not slack in the proof.
+- **Why the change is necessary.** If the original unrestricted uniform vector challenge is
+ retained, the §2.2 range-check counterexample accepts on
+ `|F|^m−(|F|−1)^m` distinct points. Plain one-round special soundness would therefore require
+ more than that many transcripts, which is not polynomial in the relevant parameters. A
+ polynomial-size one-round SS/CWSS repair must restrict or re-encode the challenge space (or
+ change the tree predicate).
+
+### 3.0 The literal `ℓ = 2` unrestricted-block reading — fails
+
+Read `SS(F,2,D)` as treating the two *blocks* `τ_0` and `τ_1` as the two coordinates, so a
+sibling may replace an entire block by an arbitrary new vector. Then the family merely gives
+`D` adversarially chosen multivariate points at which `H_0` vanishes, and `D` such points can
+all be placed on a nontrivial zero set. Even random points would supply only `D` linear
+conditions for a multilinear polynomial having up to `2^{m_0}` coefficients. This reading is
+strictly weaker than the coordinate-wise reading refuted in §2.2. ∎
+
+### 3.1 Coordinate-zipped sequential CWSS rounds — works (uniform-challenge fallback)
+
+The key observation is that the zero-check contains **two separate identities in disjoint
+variable sets**:
+
+```
+H_0 ∈ F[X_1, …, X_{m_0}],
+H_α ∈ F[Y_1, …, Y_{m_1}].
+```
+
+A CWSS star can safely process one fresh variable of each identity in parallel. What it cannot
+do is process two fresh variables belonging to the *same* arbitrary multilinear identity in
+one node.
+
+#### 3.1.1 Protocol rendering
+
+Write
+
+```
+τ_0 = (x_1, …, x_{m_0}),
+τ_1 = (y_1, …, y_{m_1}),
+r = max(m_0,m_1),
+s = min(m_0,m_1).
+```
+
+Replace the single atomic Fig. 5 challenge by `r` successive public-coin challenge rounds. For
+`j ∈ [r]`, send
+
+```
+χ_j := (x_j,y_j) ← F² if j ≤ m_0 and j ≤ m_1,
+χ_j := x_j ← F if j ≤ m_0 and j > m_1,
+χ_j := y_j ← F if j > m_0 and j ≤ m_1.
+```
+
+Equivalently, define
+
+```
+ℓ_j := 1_{j≤m_0} + 1_{j≤m_1} ∈ {1,2},
+k_j := 2.
+```
+
+At the end of these rounds, reconstruct the same vectors `τ_0,τ_1` and retain the same scalar
+claims
+
+```
+H_0(τ_0) = 0,
+H_α(τ_1) = 0,
+```
+
+which seed the unchanged downstream sumchecks.
+
+To fit a strictly alternating transcript syntax, insert a fixed empty prover message between
+successive verifier challenges. The rounds must be genuine fork points in the extraction
+object; merely sampling one atomic vector and parsing it afterward does not create the nested
+CWSS tree used below.
+
+#### 3.1.2 Why the `SS(F,2,2)` star is now sufficient
+
+At a paired round, the three challenges in the CWSS family can be relabeled as
+
+```
+(a,b), (a',b), (a,b')
+```
+
+with `a' ≠ a` and `b' ≠ b`. The geometry is still a star:
+
+```
+ (a,b')
+ |
+ |
+ (a,b) ----+---- (a',b)
+```
+
+But the two asserted polynomials use different arms:
+
+- `H_0` sees only the first coordinate, so `(a,b)` and `(a',b)` provide two distinct values of
+ its current variable;
+- `H_α` sees only the second coordinate, so `(a,b)` and `(a,b')` provide two distinct values of
+ its current variable.
+
+There is no asserted polynomial in this round containing a mixed term in both `x_j` and `y_j`.
+That is exactly what failed in the original rendering, where several coordinates of `H_0`
+(and several coordinates of `H_α`) lived in one large star.
+
+The old toy counterexample illustrates why nesting matters. For
+`H_0(X_1,X_2) = X_1X_2`, a one-round star at `(0,0)` misses `(1,1)`. In the repaired schedule,
+`X_1` and `X_2` occur in different rounds. On the branch `X_1 = 1`, the next round must test two
+distinct values of `X_2`, so the formerly missing corner appears and the nonzero polynomial is
+caught.
+
+#### 3.1.3 Seam relations and bottom-up extraction
+
+For `0 ≤ j ≤ r`, define the intermediate relation `R_j` for an opening `w̃` of `t` by
+
+```
+t = Com(w̃),
+
+H_0^{w̃}(x_1,…,x_{min(j,m_0)}, X_{min(j,m_0)+1},…,X_{m_0}) ≡ 0,
+
+H_α^{w̃}(y_1,…,y_{min(j,m_1)}, Y_{min(j,m_1)+1},…,Y_{m_1}) ≡ 0,
+```
+
+where the last two equalities are polynomial identities in the as-yet unchallenged variables.
+Then:
+
+- `R_r` is exactly the pair of scalar claims
+ `H_0^{w̃}(τ_0)=0 ∧ H_α^{w̃}(τ_1)=0` supplied by an accepting leaf;
+- `R_0` is the desired conclusion
+ `H_0^{w̃}≡0 ∧ H_α^{w̃}≡0`.
+
+The extraction kernel is the usual two-point step.
+
+**Lemma C (two-point multilinear step).** Let `G ∈ F[U,Z_1,…,Z_h]` have degree at most one in
+`U`. If `a ≠ a'` and
+
+```
+G(a, Z_1,…,Z_h) ≡ 0,
+G(a',Z_1,…,Z_h) ≡ 0,
+```
+
+then `G ≡ 0`.
+
+*Proof.* Write `G = A + U·B`, with `A,B ∈ F[Z_1,…,Z_h]`. The two identities give
+`A+aB≡0` and `A+a'B≡0`; subtraction yields `(a-a')B≡0`. Since `F` is a field and
+`a≠a'`, `B≡0`, and then `A≡0`. ∎
+
+Now extract from the transcript tree bottom-up.
+
+- **Paired round.** Recursively extract from the subtrees rooted at `(a,b)`, `(a',b)`, and
+ `(a,b')`. If any recursive call already returns a binding/MSIS escape, return it. If the
+ resulting openings of `t` differ, return the corresponding binding/MSIS escape. Otherwise
+ they are one common opening `w̃`. The center and first-coordinate sibling give two
+ identically-zero restrictions of the current variable of `H_0`, so Lemma C removes that
+ variable. The center and second-coordinate sibling do the same for `H_α`. Hence `R_j`
+ implies the parent relation `R_{j-1}`.
+
+- **Unpaired round.** There are two children with distinct scalar challenges. If their openings
+ differ, return the binding/MSIS escape; otherwise apply Lemma C to the one identity that
+ still has a fresh variable. The other identity is simply inherited.
+
+Induction to the root produces either the same binding/MSIS escape used elsewhere in Hachi or a
+single opening `w̃` satisfying `R_0`.
+
+#### 3.1.4 Zipped fallback theorem
+
+**Fallback Lemma (CWSS of the coordinate-zipped zero-check).** Let
+`Π_zc^zip` be the modified zero-check protocol above. For each challenge round `j ∈ [r]`, let
+the verifier challenge lie in `F^{ℓ_j}`, where
+
+```
+ℓ_j = 1_{j≤m_0} + 1_{j≤m_1},
+k_j = 2.
+```
+
+There is an efficient deterministic extractor which, given the public statement and a valid
+tree of accepting transcripts such that the children at every depth `j` form a family in
+`SS(F,ℓ_j,2)`, outputs either
+
+1. an opening `w̃` of `t` satisfying
+
+ ```
+ t = Com(w̃),
+ H_0^{w̃} ≡ 0,
+ H_α^{w̃} ≡ 0,
+ ```
+
+ or
+
+2. two distinct admissible openings of `t` giving the same binding/MSIS violation as in the
+ paper's commitment analysis.
+
+Consequently, under the stated binding assumption, `Π_zc^zip` is
+
+```
+(ℓ_1,…,ℓ_r)-coordinate-wise (2,…,2)-special sound
+```
+
+for the zero-check relation. Its transcript tree has
+
+```
+K = ∏_{j=1}^r (ℓ_j(2−1)+1)
+ = 3^s · 2^{r−s}
+ = 3^{min(m_0,m_1)} · 2^{|m_0−m_1|}
+```
+
+leaves.
+
+*Proof.* The bottom-up induction through the seam relations `R_j` is given in §3.1.3. ∎
+
+#### 3.1.5 Accounting and faithfulness
+
+- **Challenge payload.** The verifier still samples exactly `m_0+m_1` independent field
+ elements, and the final joint distribution of `(τ_0,τ_1)` is unchanged. They are grouped
+ into `r=max(m_0,m_1)` rounds instead of one atomic message.
+
+- **Tree size.** The fully scalar repair has `2^{m_0+m_1}` leaves. The zipped repair has
+ `3^s·2^{r-s}` leaves and
+
+ ```
+ 3^s·2^{r-s} ≤ 2^{r+s} = 2^{m_0+m_1}.
+ ```
+
+ When `m_0=m_1=m`, this improves `4^m` to `3^m`. Since the challenge arities are logarithmic
+ in the relevant table sizes, the tree remains polynomial-size under the same parameter
+ regime required by the paper.
+
+- **Knowledge error.** Using the FMN24 per-round term quoted in the formalization notes,
+ `ℓ_j(k_j−1)/|F|`, paired rounds contribute `2/|F|` and scalar rounds contribute `1/|F|`.
+ The total is therefore
+
+ ```
+ (2s + (r−s))/|F| = (m_0+m_1)/|F|,
+ ```
+
+ the same intended error as the all-scalar repair and much smaller than the paper's
+ `D`-dependent star accounting. The discrepancy between this formula and NOZ26's printed
+ restatement of FMN24 remains a separate upstream issue.
+
+- **Degree parameters.** The zero-check rounds use `k_j=2` because each identity is affine in
+ its one fresh variable. The values `2d` and `2b−1` concern other variables and other protocol
+ stages; `D=max(2d,2b−1)` should not occur in corrected Lemma 10.
+
+- **Downstream protocol.** Figs. 6–7 consume only the completed vectors `τ_0,τ_1` through
+ `eq̃(τ_0,·)` and `eq̃(τ_1,·)`. Their arithmetic is unchanged.
+
+- **Fiat–Shamir caveat.** The challenge rounds must become genuine, domain-separated random-
+ oracle queries or sequential sponge squeezes at which the extractor can fork. The payload
+ size and distribution are unchanged, but byte-for-byte transcript identity with the
+ original single-message rendering is not guaranteed and should not be claimed without fixing
+ a concrete derivation convention.
+
+### 3.2 Fully sequential scalar rounds — sound, but dominated
+
+A simpler repair sends all `m_0+m_1` coordinates in separate scalar rounds and applies Lemma C
+once per round. This is rigorous and uses ordinary `2`-special soundness throughout. Its nested
+tree is the full two-point tensor grid, with
+
+```
+K_scalar = 2^{m_0+m_1}.
+```
+
+The zipped repair performs exactly the same interpolation for each identity, but processes one
+coordinate of `H_0` and one coordinate of `H_α` in parallel. It uses fewer rounds, no more
+knowledge error, and a strictly smaller tree whenever `s>0`. The all-scalar rendering remains a
+valid fallback if an implementation exposes only scalar challenge-round machinery, but it is
+not the preferred formulation of Lemma 10.
+
+### 3.3 One round with a tensor-grid tree — sound, but unnecessary
+
+Keep one batched challenge round, but replace the star node predicate by a **grid**: the
+children must contain a product set `S_1×⋯×S_m` with `|S_j|≥2` for every coordinate.
+
+**Lemma D (grid interpolation).** A multilinear `H ∈ F[t_1,…,t_m]` vanishing on
+`S_1×⋯×S_m`, with every `|S_j|≥2`, is identically zero.
+
+*Proof.* Induct on `m`. Write `H=A+t_mB`. At each point of the first `m−1` coordinates, the
+univariate restriction in `t_m` has two roots and degree at most one, so both `A` and `B`
+vanish on the smaller grid; induction gives `A≡B≡0`. ∎
+
+This repair is sound, but a two-point grid has `2^{m_0+m_1}` leaves, larger than the zipped
+CWSS tree. It also requires a new non-star node predicate, new composition lemmas, and a new
+knowledge-error theorem, whereas §3.1 stays inside the existing CWSS definition and its
+`seqCompose` machinery. Rejected on cost.
+
+### 3.4 Keeping the original one-round star does not become sound merely because the identities are separate
+
+The disjointness of `H_0(X_1,…,X_{m_0})` and `H_α(Y_1,…,Y_{m_1})` is useful, but it does not
+rescue the original one-round star. That star still places *all* `X`-coordinates in one node,
+so `H_0` may contain mixed terms such as `X_1X_2`; similarly, `H_α` may contain mixed
+`Y_iY_j` terms. The counterexample of §2.2 therefore remains valid.
+
+What the disjointness permits is the more precise scheduling rule used in §3.1:
+
+> A CWSS round may contain several challenge coordinates only if each asserted polynomial
+> depends on at most one of those coordinates.
+
+For Hachi's two identities, this allows at most one `X`-coordinate and one `Y`-coordinate per
+round — exactly the coordinate-zipped schedule. ∎
+
+### 3.5 Keep the original vector-coordinate star and increase `k`/`D` — fails
+
+Counterexample B vanishes on every relevant axis line *identically*. A star may therefore have
+arbitrarily many siblings per vector coordinate and all its transcripts still accept. No larger
+value of `k` or `D` recovers the missing mixed-coordinate information *without changing the
+challenge encoding*. The Kronecker repair changes it so that each star coordinate is a scalar
+seed whose arm traces an information-complete curve. ∎
+
+### 3.6 Rewinding plus Schwartz–Zippel — sound, but not a CWSS extractor
+
+One can prove the zero-check probabilistically: rewind the prover with fresh uniformly random
+`(τ_0,τ_1)` and use Schwartz–Zippel to bound the acceptance probability of a nonzero
+multilinear polynomial by its total degree divided by `|F|`. This gives a sound standalone
+argument, but it replaces deterministic CWSS tree extraction by a different rewinding proof.
+Neither the paper's FMN24-based composition nor ArkLib's `CWSSStructure.append/seqCompose`
+currently composes that mixed proof style. The Kronecker rendering in §3.K turns the same
+root-counting intuition into deterministic one-round CWSS, so there is no need to leave the
+framework. ∎
+
+### 3.7 Direct scalar power fingerprint — sound one-round alternative
+
+Instead of retaining `H` and restricting its evaluation point as in §3.K, one can replace the
+equality-kernel batching directly by
+
+```
+G(τ) := Σ_i τ^{⟨i⟩} c_i.
+```
+
+Here `⟨i⟩ = Σ_j i_j2^j`. This is univariate of degree less than the padded constraint-table size,
+and it is zero exactly when every coefficient `c_i` is zero. The same independent-seed
+`SS(F,2,D)` proof and shared-seed plain-SS proof therefore apply.
+
+Contrary to the earlier version of this note, this choice does **not** destroy the tensor
+structure needed by sumcheck. The Boolean weight `τ^{⟨i⟩}` is the evaluation at `i` of the public
+multilinear polynomial
+
+```
+W_τ(X) := ∏_j ((1-X_j) + X_j · τ^(2^j)),
+```
+
+so `W_τ(i)=τ^{⟨i⟩}` and it can replace `eq̃(τ_vector,i)` in the structured multiplier at the same
+per-variable degree. Direct power batching makes the coefficient-extraction theorem especially
+simple, but it changes Eqs. (22)–(23) and the downstream public multiplier. The Kronecker-curve
+rendering is preferred because it obtains the same `D`, tree size, and error while leaving the
+paper's `H_0`, `H_α`, and sumcheck formulas intact. Algebraically the two versions are basis
+changes of the same degree-`<2^m` Reed–Solomon fingerprint. ∎
+
+## 4. Recommendation and status
+
+- **Adopted rendering (plan F6):** use the one-round independent-seed Kronecker challenge of
+ §3.K, with `ℓ=2` and `k=D=max(2^m_0,2^m_α)`. Derive the vector points from the two scalar
+ seeds and retain the paper's `H_0`, `H_α`, and downstream sumchecks unchanged. The shared-seed
+ plain-SS variant is available if minimizing the extraction tree matters more than preserving
+ independence between the two tests.
+
+- **Replace Lemma 10:** use the corrected statement in §3.K.3. The *shape* `SS(F,2,D)` can stay,
+ but its coordinates must be scalar curve seeds, its family has `2D−1` transcripts, and
+ `D` is the maximum padded constraint-table size. It is not the paper's
+ `max(2d,2b−1)`.
+
+- **Fix parameter provenance:** `2d` belongs to the scalar `α` interpolation of Lemma 9;
+ `2b+1` belongs to the degree-`2b` range sumcheck rounds of Lemma 11; and corrected Lemma 10
+ uses `D=max(N_0,N_α)` because those are the dimensions of the two multilinear coefficient
+ spaces.
+
+- **Record the protocol deviation accurately:** round count and checked equations are unchanged,
+ but the points are sampled from Kronecker curves rather than uniformly from the full vector
+ spaces. Under Fiat–Shamir, squeeze two scalar seeds and expand them by repeated squaring.
+
+- **Formalization impact:** the negative result of §2 remains useful as a regression test. The
+ repair requires (i) injectivity of `LinearMvExtension.powAlgHom` on multilinear polynomials,
+ (ii) the existing degree bound for that map, (iii) univariate root counting, and (iv) a
+ one-round `CWSSStructure` with two scalar coordinates and parameter `D`. No seam-relation
+ induction, new grid predicate, or non-CWSS soundness framework is needed. Lemma 8's existing
+ star-based folding proof is unaffected.
+
+## Appendix: statements suitable for formalization
+
+**Pseudo-code sketches, not compilable Lean.** In the repo, `MultilinearPoly` is the
+degree-restricted subtype `L⦃≤ 1⦄[X Fin ℓ]`; evaluation goes through `.val`; `eqTilde` is the
+scalar equality kernel and `eqPolynomial` its polynomial form. The exact names below are
+indicative.
+
+```
+-- The Kronecker point underlying the one-round challenge.
+def kroneckerPoint (m : ℕ) (ρ : F) : Fin m → F :=
+ fun j => ρ ^ (2 ^ j.val)
+
+-- The missing companion to the existing
+-- `LinearMvExtension.powAlgHom_of_restrict_degree_natDegree` bound.
+theorem powAlgHom_injective_on_multilinear {F} [CommRing F] [Nontrivial F] {m : ℕ} :
+ Function.Injective (fun H : MultilinearPoly F m =>
+ LinearMvExtension.powAlgHom H.val)
+
+-- Evaluation of the univariate pullback agrees with evaluation on the curve.
+theorem eval_powAlgHom_eq_eval_kronecker {F} [CommRing F] {m : ℕ}
+ (H : MultilinearPoly F m) (ρ : F) :
+ Polynomial.eval ρ (LinearMvExtension.powAlgHom H.val) =
+ H.val.eval (kroneckerPoint m ρ)
+
+-- One-round, two-seed corrected Lemma 10.
+theorem zeroCheck_kronecker_coordinateWiseSpecialSound
+ (m0 mα : ℕ) (D : ℕ := max (2 ^ m0) (2 ^ mα))
+ (hcard : D ≤ Fintype.card F) :
+ CoordinateWiseSpecialSound
+ (ell := 2)
+ (k := D)
+ zeroCheckRelation
+
+-- Optional shared-seed version: ordinary D-special soundness.
+theorem zeroCheck_kronecker_specialSound
+ (m0 mα : ℕ) (D : ℕ := max (2 ^ m0) (2 ^ mα))
+ (hcard : D ≤ Fintype.card F) :
+ SpecialSound D zeroCheckRelation
+
+-- The following lemmas support only the uniform-challenge zipped fallback.
+
+-- Lemma C: the one-variable interpolation kernel used by both arms of a zipped node.
+theorem multilinear_eq_zero_of_two_instantiations {F} [Field F] {m : ℕ}
+ (H : MultilinearPoly F (m + 1)) {u v : F} (huv : u ≠ v)
+ (hu : instantiateFirst H u = 0)
+ (hv : instantiateFirst H v = 0) : H = 0
+
+-- One paired SS(F,2,2) node removes one variable from each independent identity.
+theorem zipped_pair_step {F} [Field F] {m0 m1 : ℕ}
+ (H0 : MultilinearPoly F (m0 + 1))
+ (H1 : MultilinearPoly F (m1 + 1))
+ {a a' b b' : F} (haa' : a ≠ a') (hbb' : b ≠ b')
+ (h0_center : instantiateFirst H0 a = 0)
+ (h0_xSibling : instantiateFirst H0 a' = 0)
+ (h1_center : instantiateFirst H1 b = 0)
+ (h1_ySibling : instantiateFirst H1 b' = 0) :
+ H0 = 0 ∧ H1 = 0
+
+-- Equality-kernel basis nondegeneracy: the R_0 bridge.
+theorem eqTilde_batch_eq_zero_iff {F} [CommRing F] [Nontrivial F] {m : ℕ}
+ (c : (Fin m → Fin 2) → F) :
+ (∑ i, (eqPolynomial i) * C (c i)) = 0 ↔ ∀ i, c i = 0
+
+-- The fallback zero-check extractor, parameterized by the zipped round schedule.
+theorem zeroCheck_zipped_coordinateWiseSpecialSound
+ (m0 m1 : ℕ)
+ (ell : Fin (max m0 m1) → ℕ := fun j =>
+ (if j < m0 then 1 else 0) + (if j < m1 then 1 else 0)) :
+ CoordinateWiseSpecialSound
+ challengeSets ell
+ (fun _ => 2)
+ zeroCheckRelation
+
+-- Optional only: grid interpolation, not needed by the adopted repair.
+theorem multilinear_eq_zero_of_grid {F} [Field F] {m : ℕ}
+ (H : MultilinearPoly F m) (S : Fin m → Finset F)
+ (hS : ∀ j, 2 ≤ (S j).card)
+ (h : ∀ p ∈ Fintype.piFinset S, eval p H = 0) : H = 0
+
+-- Negative regression example: a one-round star does not identify a 2-variate multilinear polynomial.
+example : ∃ (H : MultilinearPoly F 2),
+ H ≠ 0 ∧ (∀ p on the axis cross through (a,b), eval p H = 0)
+```
diff --git a/HACHI_RECURSION_GAP.md b/HACHI_RECURSION_GAP.md
new file mode 100644
index 0000000000..fa4f2f21b9
--- /dev/null
+++ b/HACHI_RECURSION_GAP.md
@@ -0,0 +1,357 @@
+# The §4.5/§3.2 Partial-Evaluation Gap in Hachi — Analysis
+
+Companion to [`HACHI_LEMMA10_GAP.md`](HACHI_LEMMA10_GAP.md) (which documents the Lemma 10
+zero-check gap and its adopted Kronecker repair). Subject: **the partial-evaluation step of
+Hachi (NOZ26, ePrint 2026/156) §4.5 (Eqs. (24)–(26)), and its generic form §3.2** — the
+reduction from the per-slice well-formedness claims `yᵢ = fᵢ(x)` to the single `Z`-packed
+evaluation claim `f′(x) = ∑ᵢ yᵢ·Z^{⟨i⟩}` is **not knowledge-sound as stated**. The
+skeleton isolates this in one zero-round bridge,
+[`Recursion/ZBatchBridge.lean`](ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean),
+whose pull-back sorry is flagged as *expected unprovable as stated*.
+
+**Status: confirmed algebraic gap as written, found during the skeleton design review
+(2026-07-13) and re-audited against the paper (2026-07-15). The explicit counterexample below is
+information-theoretic and survives an honest downstream proof. A CWSS-native repair is adopted at
+the design level in §3: reconstruct the DP24 tensor carrier from the already-sent `yᵢ`, fingerprint
+it after the `yᵢ` are fixed, relocate the resulting claim by a degree-two sumcheck, and reuse the
+paper's existing Eq. (27) element `p` at the endpoint. The repair has not yet replaced the skeleton's
+faithful-but-unprovable `ZBatchBridge`. It has not been cross-checked with the authors.**
+
+## 1. The step in question
+
+§3.2 (and §4.5's recursion step, which follows the same pattern with `eq`-weights in place of
+monomials): to prove `f(x) = y` for a committed polynomial `f` with **base-field** (`Z_q`)
+coefficients at a point `x` with coordinates in the **extension field** `F = F_{q^k}`
+(`k = 2^κ`), the prover sends the `2^κ` partial evaluations
+
+```
+yᵢ := fᵢ(x_rest) ∈ F, i ∈ {0,1}^κ,
+```
+
+the verifier checks (or, for purity, derives `y₀` from)
+
+```
+y = ∑ᵢ mᵢ(x_top) · yᵢ (Eq. (24) / §3.2 display)
+```
+
+and the remaining obligation — "all the yᵢ are well-formed" — is **replaced** by the single
+packed claim (Eq. (26) / §3.2's f′-display), which the downstream protocol then proves:
+
+```
+f′(x_rest) = ∑ᵢ yᵢ · Z^{⟨i⟩}, where f′ := ∑ᵢ fᵢ · Z^{⟨i⟩}.
+```
+
+The paper's implicit claim is that this replacement is an equivalence ("proving well-formedness
+of all (yᵢ)ᵢ is equivalent to proving (26)").
+
+## 2. The gap
+
+The packed claim pins only **one** `F`-linear combination of the per-slice defects
+`εᵢ := yᵢ − fᵢ(x_rest) ∈ F`:
+
+```
+∑ᵢ Z^{⟨i⟩} · εᵢ = 0. (*)
+```
+
+If the `εᵢ` were base-field scalars, (\*) would force `εᵢ = 0` (the `Z`-powers are an
+`F_q`-basis). But `fᵢ(x_rest)` is evaluated at extension-field coordinates, so `εᵢ` ranges over
+all of `F`: (\*) is `k` `F_q`-linear conditions on `k²` `F_q`-dimensions — a `k(k−1)`-dimensional
+kernel for every `k ≥ 2`.
+
+**Concrete cheat (`κ = 1`, `k = 2`, `F = F_q[Z]`).** Let `f(X₁, X₂)` be committed honestly with
+slices `f₀, f₁` and true partials `tᵢ = fᵢ(x₂)`. The adversary sends
+
+```
+y₁ := t₁ − δ, y₀ := t₀ + Z·δ, δ ∈ F arbitrary.
+```
+
+Then `y₀ + Z·y₁ = t₀ + Z·t₁` — the packed claim (26) is **true for the honest committed `f′`**
+and is proven by an entirely honest downstream run. The verifier's Eq. (24) check accepts the
+claimed value
+
+```
+y = y₀ + x₁·y₁ = f(x) + δ·(Z − x₁),
+```
+
+so for any `x₁ ≠ Z` **every** target value `y` is reachable: the extractor holds the honest
+`f`, all checks pass, and `f(x) ≠ y`. The same computation goes through with `eq`-weights
+(§4.5) in place of monomials, and with the derive-`y₀` (footnote 5/10) convention — the cheat
+vector simply enters through the sent `y₁`.
+
+Note the contrast with the **generic §3.1 transformation** (Lemma 5/Theorem 2, the trace
+check): there the downstream claim pins the packed element `Y` *exactly* (the residual claim is
+an equality of ring elements), and the trace check transfers it to `y` with no slack — §3.1 is
+unaffected. The slack is created precisely by §3.2/§4.5's `k²`-dimensional `yᵢ`-layer between
+the two.
+
+Also note Remark 1 of the paper flags a *different* issue with applying §3.1 to base-field
+polynomials (extracted `f` lands in `F_{q^k}[X]`), and offers §3.2 as the fix — the gap above
+says the fix itself does not extract.
+
+## 3. Adopted repair: carrier-free-on-the-wire CWSS relocation
+
+The repair uses the tensor carrier from DP24/Binius, but the prover does **not** send that carrier:
+the verifier can reconstruct it from the `yᵢ` that Hachi already sends. Likewise, the usual
+terminal field evaluation produced by the relocation sumcheck is not sent: the verifier derives it
+from Hachi's existing Eq. (27) ring element `p`. Thus the values carried across the recursion
+boundary remain exactly the paper's `(k−1)` extension-field elements and one ring element `p`.
+
+The price is interaction: one post-`yᵢ` batching challenge and a degree-two relocation sumcheck.
+This is a change to the paper's protocol, but it stays entirely within Hachi's CWSS proof currency.
+
+### 3.1 The existing `yᵢ` determine the tensor carrier
+
+Write `B := F_q`, `L := F_{q^k}`, `k := 2^κ`, and choose a `B`-basis
+`(βᵢ)_{i < k}` of `L` (the paper's `Z`-basis). Split the Boolean index as `j ‖ i`, where
+`j ∈ {0,1}^{mLow}`, `i ∈ {0,1}^κ`, and `mLow = ℓ−κ`. For the short base-field table `w̃`, define
+
+```
+ŵⱼ := ∑ᵢ w̃_{j‖i} · βᵢ ∈ L,
+tᵢ := ∑ⱼ eq(j, a₀) · w̃_{j‖i} ∈ L. (true partials)
+```
+
+This is §4.5's multilinear/`eq` notation. For generic §3.2, replace `eq(j, a₀)` by the
+corresponding tail-monomial weight; the tensor and CWSS arguments below use only `B`-linearity and
+are otherwise identical.
+
+Consider the tensor algebra `A := L ⊗_B L`. Once the verifier has the full derived family
+`(yᵢ)ᵢ`, it can form locally
+
+```
+S_y := ∑ᵢ yᵢ ⊗ βᵢ ∈ A. (public; not sent)
+```
+
+The committed table determines
+
+```
+S_w := ∑ⱼ eq(j, a₀) ⊗ ŵⱼ
+ = ∑ᵢ tᵢ ⊗ βᵢ. (witness carrier)
+```
+
+The second equality follows by expanding `ŵⱼ` and exchanging the sums. Since
+`(1 ⊗ βᵢ)ᵢ` is an `L`-basis of `A`,
+
+```
+S_y = S_w ↔ ∀ i, yᵢ = tᵢ.
+```
+
+This is exactly the missing `k²`-dimensional statement. The paper's Eq. (26) applies only one
+non-injective projection to it; the repair tests the full tensor equality across a CWSS family.
+The tensor is a proof device and verifier-local computation, not a new prover message.
+
+### 3.2 Post-`yᵢ` scalar fingerprint
+
+After the `yᵢ` are fixed, the verifier samples a fresh scalar `ρ ∈ L`. The ordering is
+load-bearing: an earlier Hachi challenge cannot be reused, because then a malicious prover could
+choose its `yᵢ` after seeing the fingerprint.
+
+For each `ρ`, define the `B`-linear map `λ_ρ : L → L` by
+
+```
+λ_ρ(βᵤ) := ρᵘ, 0 ≤ u < k,
+```
+
+and the induced `B`-balanced map `Λ_ρ : A → L` by
+
+```
+Λ_ρ(x ⊗ z) := λ_ρ(x) · z.
+```
+
+The verifier computes the initial target directly from the existing partials:
+
+```
+s₀(ρ) := Λ_ρ(S_y) = ∑ᵢ λ_ρ(yᵢ) · βᵢ. (public)
+```
+
+On the witness side, define the public table
+
+```
+A_ρ(j) := λ_ρ(eq(j, a₀)).
+```
+
+Then
+
+```
+Λ_ρ(S_w) = ∑ⱼ A_ρ(j) · ŵⱼ.
+```
+
+The direct Vandermonde weights `ρᵘ` are the simplest choice. An equivalent DP24-shaped choice is
+`λ_ρ(βᵤ) := eq(u, (ρ, ρ², …, ρ^{2^{κ−1}}))`; its Kronecker pull-back also has degree `< k` and is
+injective. The rest of the protocol is unchanged by this choice.
+
+The multiplier is efficiently evaluable without materializing `S_y`. If `(βᵤ*)ᵤ` is the
+trace-dual basis, then
+
+```
+λ_ρ(x) = ∑ᵤ ρᵘ · Tr_{L/B}(βᵤ* · x).
+```
+
+Equivalently, write this `B`-linear map as a linearized polynomial
+`λ_ρ(x) = ∑_{h if S.check s.original_claim s.t_eval_point (tr ⟨0,_⟩) then pure ⟨s, tr ⟨0,_⟩⟩ else failure`;
+ prover sends `S.carrierEval t' (suffix)`; witness pass-through (`BatchingWitIn`).
+ Output relation `relPackedClaim := { (⟨s, ŝ⟩, wit) | ŝ = S.carrierEval wit.t' … ∧ wit.t' = S.pack wit.t ∧ compat }`.
+- `Relocation.lean`: the challenge round + `compute_s0`; StmtIn `PackedClaimStatement`, StmtOut
+ `Statement (RingSwitchingBaseContext …) 0` (types unchanged); DP24-only, keeps RBR statements.
+- Re-plumb `BatchingPhase.lean` (monolith = append; restate the sorried KState/RBR/completeness
+ at the same outer boundaries), `General.lean`, `FRIBinius/General.lean`. Build green; no
+ hand-edits to `ArkLib.lean`.
+
+**B3. Generic packing CWSS (1 d).** In `Packing.lean`:
+
+```lean
+theorem PackingPhase.coordinateWiseSpecialSound_of_checkSound
+ (hlaw : S.CheckSound) (D : CWSSStructure _) :
+ (packingVerifier S).coordinateWiseSpecialSound init impl D
+ relOriginalClaim relPackedClaim
+```
+
+via `coordinateWiseSpecialSound_of_isEmpty_challengeIdx` (probability-phrased hypothesis already
+accommodates the guard; acceptance forces `check = true`, then `hlaw` converts the packed-claim
+witness into the original-claim witness through `unpack`). One P→V message ⇒
+`IsEmpty ChallengeIdx` holds. Also state the RBR-error-0 analogue for Binius symmetry (optional).
+
+**B4. Guarded CWSS composition (2–3 d, independent).** Extend
+`CoordinateWiseSpecialSoundness/Composition.lean` and `SeqCompose.lean`:
+- `Verifier.append_treeSpecialSound_of_guard` — hypothesis
+ `hV₁ : ∀ stmt tr, V₁.verify stmt tr = if check stmt tr then pure (verify₁ stmt tr) else failure`.
+ Proof deltas against :366: a guarded `append_run_pure_left` (composed acceptance probability 1
+ forces `check = true` — the `failure` branch has success probability 0 — then reduces to the
+ pure case; uses nonemptiness of the suffix tree's transcript list, cf. the `LeafPath` machinery
+ already used at Composition.lean:383) and a guarded `pure_accepting_of_mem`. Corollaries:
+ `append_coordinateWiseSpecialSound_of_guard` + the OracleVerifier wrapper.
+- `Verifier.seqCompose_treeSpecialSound_of_guard` — the n-ary variant with per-factor
+ `IsGuarded` (a `check`-indexed generalization of `IsPure`; pure = trivially-true check), by the
+ same induction as SeqCompose.lean:364 with the guarded append as the step.
+Four consumers in this plan: the §3.1 head (D1), the guarded sumcheck-round loop (F7), the
+final-eval step (F8), and the §4.5 handoff head (G3). Generic security infrastructure —
+coordinate with maintainers (R3).
+
+**B5 (optional, parallel). Binius packing law (2–4 d).** Prove
+`biniusPackingScheme_checkSound` from `decomposeColumns_spec` + tensor-algebra + MLE partial
+evaluation. Payoff: the first *proven* soundness statement in the Binius ring-switching stack
+(via B3). Not on Hachi's critical path.
+
+### Phase C — Hachi profile + packed-evaluation algebra (~4–6 days; the mathematical heart)
+
+**C1. `hachiProfile` (1 d).** New `Commitments/Functional/Hachi/RingSwitch/Profile.lean`
+(imports `RingSwitching/Profile`, `Lattices/CyclotomicRing/Subfield`). Parameter dictionary
+(**`κ` clash**: paper `κ` = log extension degree; profile rank is `α − κ` — spell it out
+everywhere, R5):
+
+```lean
+noncomputable def hachiPackBasis (h2 …) (hk …) :
+ Basis (Fin (α − κ) → Fin 2) ↥(fixedSubring (R := ZMod q) α (2^κ)) (Rq (powTwoCyclotomic α)) :=
+ -- Basis.ofEquivFun on psiLinearEquiv.symm (A2.5), reindexed along hypercubeEquivFin (A1)
+
+noncomputable def hachiProfile (h2 …) (hk …) :
+ RingSwitchingProfile ↥(fixedSubring (R := ZMod q) α (2^κ)) (Rq (powTwoCyclotomic α)) (α − κ) where
+ basis := hachiPackBasis h2 hk
+ A := Rq (powTwoCyclotomic α); φ₀ := RingHom.id _; φ₁ := (conjAut α : _ →+* _)
+ decomposeColumns z v := ↑(hachiPackBasis h2 hk |>.repr z v)
+ decomposeRows z u := ↑(hachiPackBasis h2 hk |>.repr (conjAut α z) u)
+ decomposeColumns_spec := … -- ~10 lines: coords in B are conjAut-fixed (A2.2) + Basis.sum_repr
+ decomposeRows_spec := … -- conjAut ring-hom + involution (A2.1) + Basis.sum_repr
+```
+
+Acceptance: `example` instantiation at the paper's Fig. 9 shape (`q ≡ 5 (mod 8)`, `α = 10`,
+`κ = 2`).
+
+**C2. `hachiPackingScheme` (1 d).** `RingSwitch/Scheme.lean`:
+`pack` = ψ on coefficient blocks of a `CMlPolynomial B (μ + (α−κ))` (block structure by the A1
+convention; agreement-with-`packMLE` lemma is Phase H hygiene, not a dependency);
+`weights xt j := ↑((CMlPolynomial.monomialBasis xt).get j)` (tail monomials, values in `B`);
+`decomp` = rows or columns per the C3 proof (record the outcome as a one-line note in
+Profile.lean's table, R2); `carrierEval := embedded_MLP_eval (hachiProfile …)`. Plus the
+paper-form check `traceCheck s Y := traceH α (2^κ) (Y * conjAut α (psi … (monomialVec s.xt))) = (2^α/2^κ) • ↑s.y`
+with a `Decidable` instance via `traceHComp`.
+
+**C3. Packed-evaluation lemma (2–4 d).** `RingSwitch/PackedEval.lean` — Theorem 2 lifted to the
+multilinear level; this discharges both scheme laws and the paper-check equivalence:
+
+```lean
+theorem traceH_packPoly_eval (h2) (hk)
+ (f : CMlPolynomial B (μ + (α−κ))) (x : Fin μ → B) (xt : Fin (α−κ) → B) :
+ traceH α (2^κ) ((packPoly f).eval (coe ∘ arrange x xt) * conjAut α (psi … (monomialVec xt)))
+ = (2^α / 2^κ) • ↑(f.eval (paper-order x xt))
+```
+
+Proof plan: (i) expand `(packPoly f).eval` by `evalSplit_eq_eval`/`eval_eq_sum`
+(PolynomialEvalSplit, instantiated at the subring `B` — check `CMlPolynomial`'s ring-hom
+`map`/`eval_map` support early, R1) into `Σ_i headMonomial i * ψ(block i)`; (ii) push `traceH`
+through the sum (additivity); (iii) extract the `B`-valued, σ-fixed `headMonomial i` via
+`traceH_smul_fixed`; (iv) apply `traceH_psi_mul_conj` per block; (v) reassemble via
+`evalSplit_eq_eval` over `B`. Corollaries:
+- `hachiPackingScheme_checkSound` / `_checkComplete` (the B3/B1 law props);
+- `traceCheck_iff_check` (paper trace equation ⟺ generic decomposition check, via A2.6
+ unit-cancellation and `Subtype.val`-injectivity).
+
+### Phase D — §3.1 head, composed end-to-end (~3–4 days)
+
+**D1. The head as an instance + adapter (2–3 d).** `RingSwitch/Head.lean`:
+- `RingSwitchStatement := { pp, u, xl : Vector B r, xh : Vector B m, xt : Vector B (α−κ), y : B }`
+ (point pre-split to match `PolyEvalStatement`'s `r`/`m` split; `xt` = packed tail).
+- The head verifier **is** `packingVerifier hachiPackingScheme` specialized with
+ `Aux := (pp, u)` payload (statement-shape functor around `PackedClaimStatement`), i.e. one
+ message `Y' ∈ Rq` and the guarded check — **no new protocol code**, only statement plumbing.
+- Zero-round σ₋₁ adapter (`ReduceClaim`, D5): `toPolyEvalStatement (s) (Y') :=
+ { pp := s.pp, u := s.u, xl := coe ∘ s.xl, xh := coe ∘ s.xh, y := conjAut α Y' }`.
+- `relRingSwitch` — same three-case shape as `relPolyEval`; opening case:
+ `VerifiedOpening … ∧ (unpackPoly (extractedPoly Φ base o)).eval (xl ++ xh ++ xt) = ↑y`.
+- Pull-back `mem_relRingSwitch_of_relPolyEval` (opening case = C3's soundness corollary; MSIS
+ cases pass through) → head CWSS via B3 + `ReduceClaim.verifier_coordinateWiseSpecialSound`.
+- Prover skeleton + `traceCheck_of_honest` (D12 scope).
+
+**D2. Composition + doc fixes (0.5–1 d).** In `Hachi/Basic.lean`:
+`ringSwitchEvalVerifier := headVerifier.append (adapter.append evalVerifier)` and
+
+```lean
+theorem hachi_ringSwitch_eval_coordinateWiseSpecialSound :
+ ringSwitchEvalVerifier.coordinateWiseSpecialSound init impl
+ (…ofIsEmpty-append chain…) (relRingSwitch …) (relOut …)
+```
+
+via **B4's guarded append** at the head seam + the existing
+`eval_coordinateWiseSpecialSound`. Migrate the (now ≥3) binary appends to `seqCompose`
+where factors are pure (the guarded head stays an outer binary append). Fix the "§4.1" → "§3"
+cross-references (Basic.lean:37/:212, PolyEvalReduction.lean:46-47).
+
+### Phase E — §3.2 base-field head (~3–5 days, parallel with F)
+
+One-message, **pure** (D7) head for `f` with `ZMod q` coefficients at a `B`-valued point
+(Eq. (11); reduces variables to `ℓ − α` instead of `ℓ − α + κ`):
+- Message: `(y_i)_{i ≠ 0} : Fin (2^κ − 1) → B`; verifier *derives* `y₀`, outputs the claim
+ `f′(x_{κ+1..ℓ}) = Σ_i y_i · Z^{Σ i_t 2^{t−1}}` with the `Z`-powers realized by
+ `vElt`/`fixedBasisMap` (Eq. (7) generators, §1.2).
+- Formally a second `PackingScheme`-adjacent step at the **field-level profile shape**
+ `B := ZMod q`, `L := ↥(fixedSubring α (2^κ))`, basis = `Z`-powers — reuse `packMLE` here
+ (coefficients are already the right shape) or the CMlPolynomial analogue per A1 conventions.
+- CWSS via `ReduceClaim`/one-message-pure + NoChallenge; zero soundness error; new algebra: the
+ `Z`-power reindexing lemma `f′(x) = Σ_i y_i Z^{…}` (paper §3.2 display).
+- Then Phase D applies downstream unchanged.
+
+### Phase F — Hachi's sumcheck, §4.3 (~23–32 days total; ~21–27 excluding the deferrable F1)
+
+**F1. Field upgrade (2–5 d, or defer via D10).** Close `no_selfReciprocal_factor`
+(Field.lean:207; 4-step docstring plan, blueprint difficulty 8/10) to obtain
+`Field ↥(fixedSubring α (2^κ))` / `fixedSubringEquivGaloisField` under `q % 8 = 5`. Everything
+in F2–F9 is stated over an abstract `[Field F]` (D10), so F1 can land last; it gates only the
+final Hachi-concrete instantiation. Also needed here: `SampleableType F` / `Fintype F` transport
+along the subring (finite subring of a finite ring — easy), since F's challenges are sampled.
+
+**F2. Eq. (20) → `R^lin` adapter (2 d).** Zero-round `ReduceClaim` from QuadEval's output
+statement `(QuadEvalStatement × CarrierCom × challenges)` to
+
+```lean
+structure RlinStatement (Φ) (n μ : ℕ) where
+ M : PolyMatrix (Rq Φ) n μ; yvec : PolyVec (Rq Φ) n; bound : ℕ -- ‖·‖∞ ≤ bound
+```
+
+assembling the Eq. (20) block matrix from `(pp, v, u, y, avec, bvec, c)` (rows = c1..c5 blocks;
+`jMatrix`, `gadgetMatrix`, `tensorG1`, `tensorG` from QuadEvalGadgets). Witness map: stack
+`QuadEvalResponse` into `ζ = (ŵ, flatten t̂, ẑ)`; `mapWitInv` un-stacks. Deliverables: the
+block-row equivalence lemmas `rlin_iff_relOut_linear` (c1–c5 ⟺ `M ζ = yvec`) and
+`range_iff_relOut_norm` (c6 ⟺ `‖ζ‖∞ ≤ γ`), then
+`ReduceClaim.verifier_coordinateWiseSpecialSound` with pull-back = the ⟸ directions. This
+adapter is pure — plain append.
+
+**F3. Quotient-lift algebra (2–3 d, independent).** `Data/Lattices/CyclotomicRing/` addition
+(generic, reusable by LatticeFold-style work): for the quotient `π : (ZMod q)[X] → Rq Φ`,
+- `exists_quotient_witness : M ζ = y (in Rq) ↔ ∃ ρ, deg-bounds ∧ M̂ ζ̂ = ŷ + (X^d + 1) · ρ (in (ZMod q)[X])`
+ (coefficient-lift of matrices/vectors; `ρ` degree `< d − 1`, plus its base-`b` gadget
+ decomposition per the paper's hidden-decomposition remark);
+- evaluation compatibility: `evalAt (α : F) : (ZMod q)[X] →+* F` via the `ZMod q ↪ F` embedding,
+ and the degree bound `natDegree (Σ M̂ᵢⱼ ζ̂ⱼ − ŷᵢ − (X^d+1)ρᵢ) ≤ 2d − 1`;
+- the interpolation kernel: a degree-`≤ 2d−1` polynomial over a field vanishing at `2d` distinct
+ points is zero (Mathlib: `Polynomial.eq_zero_of_natDegree_lt_card_of_eval_eq_zero`-family).
+
+**F4. HMZ25 lift reduction — Fig. 4 / Lemma 9 (3–4 d).** Two-round reduction
+`pSpec := ⟨![.P_to_V, .V_to_P], ![WCommitment, F]⟩`:
+- Message: `t := Com(w̃)` — the **inner-outer commitment without initial decomposition** of the
+ next-iteration witness `w̃` (Eq. (21): the `(ZMod q)`-coefficient rows of `ζ` and of the
+ quotient digits `ρ_u`); reuse `InnerOuter` commitment types + `WeakBinding`.
+- Challenge: `α ← F`. Output statement: `{ rlin-data, t, α }`; **output witness** (never sent,
+ D6): `w̃` itself. Output relation `relLift`: `t = Com(w̃) ∧ (rows of M̂ ζ̂(w̃) − ŷ − (X^d+1)ρ(w̃)
+ evaluated at α are 0) ∧ ranges(w̃)` ∨ binding/MSIS escapes.
+- CWSS: `ofSpecialSound (k := 2d)` on the single scalar challenge; extraction: `2d` accepting
+ branches either yield two distinct `w̃` openings of `t` (→ weak-binding escape, Lemma 7 route)
+ or one `w̃` with `2d` roots (F3's interpolation) ⇒ `R^lin` membership. The star machinery
+ needed is the `ℓ = 1` case (`isSpecialSoundFamily_one_iff_injective`); generalize
+ `CoordinateWise.SingleRound`'s star readers from `Fin (2^r) → C` challenges to plain scalar
+ challenges (small refactor: its `pSpec` at `r := 0` + `Equiv.funUnique`, or a scalar twin).
+
+**F5. Constraint encoding — Eqs. (21)–(23) (2–3 d).** Definitions only (no protocol):
+`w̃` as a `CMlPolynomial F (log (μ+n) + log d)`-shaped table per Eq. (21) (index bookkeeping via
+A1's conventions); `α̃(ℓ) = α^ℓ` and `M̃_α(i,u)` as multilinear extensions (`mle`-style, using
+the repo's MLE infrastructure); the batched `H_α` (Eq. (22)) and `H_0` (Eq. (23)); the sumcheck
+polynomials `F_{0,τ₀} = eq̃·range-product·1_{≤μ}` and `F_{α,τ_α} = w̃·α̃·(Σ eq̃ M̃_α)`, expressed
+through `SumcheckMultiplierParam` with a Hachi `Context` type carrying scalar seeds `(ρ₀,ρ_α)`,
+their derived Kronecker points `(τ₀,τ_α)`, and `(t, α, public M̃_α data)` (Context is generic,
+§1.4). **Pin the exact per-round degree here**: the range
+product `∏_{j=-(b-1)}^{b-1} (X − j)` has `2b−1` factors; with the multilinear `w̃` and `eq̃`
+multiplier the round polynomial degree is `2b`, hence `k = 2b+1` transcripts per round
+(verified independently; the repo docstring's "Q of degree 2b / round degree 2b+1" at
+Structured.lean:79-80 is off by one against its own printed product — fix it here — and the
+paper's "b+1 elements per round" matches neither, likely an unstated digit-range convention).
+Thread the result as `d₀ := degCombinator + 1` uniformly; everything degree-parametric
+downstream, so any residual convention change costs a constant rename. **Also pin the challenge
+arities here**: the paper's `τ₀ ← F^{log μ + log d}` is in tension with `w̃`'s own index arity
+`log(μ+n) + log d` (Eq. (23)'s `eq̃(t,(u,ℓ))` needs `t`-arity equal to `w̃`'s index arity; the
+`1_{≤μ}` indicator restricts the *range check*, not the index space). Pin these as `m₀,m_α` and
+set F6's interpolation parameter to `D := max(2^m₀,2^m_α)`; require `D ≤ |F|`.
+
+**F6. One-round Kronecker zero-check (3–4 d).** Keep Fig. 5 as one challenge round, but sample
+two independent scalar seeds `(ρ₀,ρ_α) ∈ F²` and derive
+
+```
+τ₀ := (ρ₀, ρ₀², ρ₀⁴, …, ρ₀^(2^(m₀-1))),
+τ_α := (ρ_α, ρ_α², ρ_α⁴, …, ρ_α^(2^(m_α-1))).
+```
+
+This block runs at the fixed `α` produced by F4. Keep F4's `α` fork as an earlier/nested CWSS
+node even if the concrete transcript serializes `α,ρ₀,ρ_α` contiguously: one flat three-coordinate
+star does not interpolate the mixed `(α,ρ_α)` dependence.
+
+Use a single `CWSSStructure` with `ℓ=2` and `k=D=max(2^m₀,2^m_α)`, hence `2D−1` branches.
+The mathematical work is:
+
+- reuse `LinearMvExtension.powAlgHom` and
+ `powAlgHom_of_restrict_degree_natDegree` from
+ `ArkLib/Data/MvPolynomial/LinearMvExtension.lean`;
+- prove `powAlgHom` injective on the per-variable-degree-`≤1` subtype (the same file's
+ `linearMvExtension` inverse machinery supplies the coefficient argument);
+- prove evaluation compatibility with the derived Kronecker point;
+- generalize the single-round CWSS assembly helper beyond its current `k=2` specialization, or
+ prove the local `ℓ=2,k=D` transcript-tree theorem directly;
+- use `D` distinct roots on the first star arm for `H₀` and on the second arm for `H_α`; differing
+ leaf openings return the existing weak-binding/MSIS escape;
+- bridge `H₀ ≡ 0 ∧ H_α ≡ 0` to the entrywise range and row constraints, while the accepting leaf
+ claims `H₀(τ₀)=H_α(τ_α)=0` feed F7 unchanged.
+
+The equality-kernel multipliers and sumcheck formulas remain exactly those of Eqs. (22)–(23).
+What changes is the challenge distribution: the points lie on Kronecker curves rather than being
+uniform in the full vector spaces. Record the `D/|F|` error scale and require a larger concrete
+extension or same-message parallel repetition if the `D≈2^26`, `|F|≈2^128` instance must meet a
+full 128-bit target.
+The shared-seed plain-`D`-SS variant is a smaller optional fallback; the independent-seed CWSS
+version is the default because it preserves cross-block independence. Full proof and alternatives:
+[`HACHI_LEMMA10_GAP.md`](HACHI_LEMMA10_GAP.md).
+
+**F7. Per-round sumcheck CWSS on the substrate (5–6 d; the second heart).** New
+`Sumcheck/Structured/CWSS.lean` (or Hachi-local first, promoted later):
+
+```lean
+theorem guardedRound_coordinateWiseSpecialSound (d : ℕ) (i : Fin ℓsc) (hcons : …) :
+ (guardedRoundOracleVerifier … d i).coordinateWiseSpecialSound init impl
+ (CWSSStructure.ofSpecialSound (fun _ => d + 1) …)
+ (sumcheckRoundRel … i.castSucc) (sumcheckRoundRel … i.succ)
+```
+
+(stated over the **guarded** round verifier introduced below — per R10 the theorem is
+unprovable for the substrate's pure-with-dummy `roundOracleVerifier`).
+
+- New per-round relation family `sumcheckRoundRel` in the CWSS currency: "committed `w̃` opens
+ `t` ∧ `H`-projection structural invariant ∧ `sumcheckConsistencyProp` at the current target"
+ (∨ escapes) — the CWSS analogue of `masterKStateProp`, but Hachi-shaped and paper-faithful
+ (Lemma 11's statement).
+- Extraction per round: `d+1` distinct scalar challenges; branches share the message `g_i`;
+ either two branches disagree on the (relation-level) `w̃` ⇒ binding escape, or the univariate
+ `Σ_b H(a_{ nomatch h | ⟨1,_⟩ => infer_instance` matching
+ ([Sumcheck/Structured/SingleRound.lean:106-116](ArkLib/ProofSystem/Sumcheck/Structured/SingleRound.lean#L106)).
+- Pass-through verifier idiom: `fun stmt tr => pure (stmt, tr.messages ⟨0,rfl⟩, tr.challenges ⟨1,rfl⟩)`
+ ([QuadEval/Reduction.lean:251](ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean#L251));
+ prover skeleton with `computeV`/`computeResp` params (:265-298).
+- `SampleableType`: `FinEnum (ZMod n)` instance exists (VCVio `SampleableType.lean:332`), so
+ `SampleableType (ZMod q)` is derivable; for an abstract field take `[SampleableType F]` as a
+ hypothesis (v2's D10).
+- Weak binding: `VerifiedOpening` / `outputToModuleSIS_valid_of_verified`
+ ([InnerOuter/Security.lean:163/332](ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean#L163)) —
+ the pattern (not necessarily the instance) F4's collision escape follows.
+
+---
+
+## 1. Global design decisions (new in this plan; supersede v2 where they conflict)
+
+**G1 — Escape threading via `⊕` (the one v2 under-specified point).** Once F4's extractor can
+hit a *binding break of the new `w̃`-commitment*, that escape must flow **backwards** through
+every seam up to the chain head (composed extraction: `E₂` feeds `E₁`). Today's
+`relOut`/`relRlin` seams have no home for it. Fix: thread a single **escape budget type** `E`
+through the chain as a plain `Sum`:
+
+```lean
+-- sketch (F2.0); Set.withEscape is ~5 lines
+def Set.withEscape (rel : Set (S × W)) (esc : Set E) : Set (S × (W ⊕ E)) :=
+ {p | match p with
+ | (s, .inl w) => (s, w) ∈ rel
+ | (_, .inr e) => e ∈ esc}
+```
+
+Crucially `esc` is **statement-independent** (an MSIS/collision solution is checkable against
+the parametric commitment key alone), so pass-through across statement maps is trivial. The
+threaded variants of the finished theorems are built in **new files** by wrapping the exported
+extraction cores (`buildWitness_mem_relIn`, `ReduceClaim.verifier_coordinateWiseSpecialSound`)
+— zero edits to sorry-free proofs. Concrete instantiation: `E := LiftEscape` (F4.2).
+
+**G2 — the `w̃`-commitment key is a *parameter*, not a statement field.** `ReduceClaim.mapStmt`
+is a pure function `StmtIn → StmtOut`, so F2 cannot conjure a fresh commitment key into the
+`R^lin` statement. Instead the key rides as a section `variable` of the F2/F4 files (repo
+precedent: relations already take `base ω γ` as plain arguments). F4's verifier never reads it
+(pure pass-through); only the relations do.
+
+**G3 — guards are `Bool`-valued.** `Verifier.IsGuardedWith V check verify` with
+`check : StmtIn → FullTranscript pSpec → Bool` and body
+`if check s tr then pure (verify s tr) else failure`. Consumers with decidable Prop checks use
+`decide`. Purity is the `check := fun _ _ => true` special case.
+
+**G4 — plain `Verifier` only.** `OracleVerifier.append` is sorried; all B4/F2/F4 statements stay
+at the plain-`Verifier` level (exact precedent: the comment before `quadEvalPackage`,
+Soundness.lean:451-453). Oracle wrappers are deferred, tracked in the B4 file header.
+
+**G5 — field abstraction (v2's D10, made concrete).** F3/F4 are stated over
+`{F : Type} [Field F]` plus an embedding `φF : ZMod q →+* F` (injective for free:
+`RingHom.injective` from a field domain) and `[SampleableType F]` where challenges are drawn.
+No dependence on `fixedSubring`/`GaloisField`/`no_selfReciprocal_factor`.
+
+**G6 — seams are definitional.** Because `▷`'s `hseam` is by `rfl`, each milestone *exports* its
+`relOut` as a named `def` and the next milestone's `relIn` *is that name*. Never restate a seam
+relation.
+
+**Standing hypotheses** (unchanged from v2's D11): `[Fact (Nat.Prime q)]`, `Φ := 𝓜(q, α)` with
+`1 ≤ α` (so `d = 2^α ≥ 2`), plus `hq5 : q % 8 = 5`, `hκ : (2ω)² < q`, `hτ : 0 < zDigits`
+wherever the QuadEval layer is consumed.
+
+**Validation protocol per milestone** (CLAUDE.md guardrails): `git add` new files, then
+`./scripts/validate.sh` (add `--lint` before PR); never hand-edit `ArkLib.lean`; update
+`docs/wiki/repo-map.md` in the same PR that creates a new directory.
+
+---
+
+## 2. Milestone B4 — guarded CWSS composition (~2.5–3.5 d)
+
+**Goal.** `append`/`seqCompose` CWSS theorems whose *left* factors may reject at runtime, plus
+the guarded `▷`. Generic security infrastructure — **coordinate with maintainers before
+starting** (v2's R3); no existing theorem changes byte-wise.
+
+**New file** `ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean`
+(imports `Composition`, `SeqCompose`, `Package`).
+
+### B4.1 The guard predicate and rejection lemmas (0.5 d)
+
+```lean
+-- sketches; binders as in Composition.lean's section variables
+def Verifier.IsGuardedWith (V : Verifier oSpec StmtIn StmtOut pSpec)
+ (check : StmtIn → FullTranscript pSpec → Bool)
+ (verify : StmtIn → FullTranscript pSpec → StmtOut) : Prop :=
+ ∀ stmt tr, V.verify stmt tr = if check stmt tr then pure (verify stmt tr) else failure
+
+class Verifier.IsGuarded (V : Verifier oSpec StmtIn StmtOut pSpec) : Prop where
+ is_guarded : ∃ check verify, V.IsGuardedWith check verify
+
+instance : V.IsPure → V.IsGuarded -- check := fun _ _ => true
+```
+
+Rejection lemma (the guarded half of `pure_accepting_of_mem`'s dichotomy):
+
+```lean
+theorem Verifier.failure_not_accepting (lang : Set StmtOut) :
+ Pr[(· ∈ lang) | OptionT.mk do
+ (simulateQ impl ((failure : OptionT (OracleComp oSpec) StmtOut)).run' (← init))] = 0
+```
+
+*Proof plan:* the computation's support contains no `some` outcome; unfold as in
+`pure_accepting_of_mem`'s step (a) (Composition.lean:333-362) with `simulateQ_failure` /
+`OptionT.run_failure` simp lemmas in place of `simulateQ_pure`. Then the two directional
+workhorses (both ~10-line wrappers over the existing pure lemmas after an `if`-split):
+
+```lean
+theorem Verifier.guarded_accepting_of_mem (hV : V.IsGuardedWith check verify)
+ (hcheck : check stmt tr = true) (hout : verify stmt tr ∈ lang) : Pr[…] = 1
+theorem Verifier.check_eq_true_of_guarded_accepting (hV : V.IsGuardedWith check verify)
+ (hacc : Pr[…] = 1) : check stmt tr = true
+theorem Verifier.mem_of_guarded_accepting … : verify stmt tr ∈ lang
+ -- mirrors mem_of_pure_accepting (SeqCompose.lean:53-84), incl. its nonempty-init-support step
+```
+
+### B4.2 Guarded left-run lemma (0.25 d)
+
+```lean
+theorem Verifier.append_run_guardedLeft (hV₁ : V₁.IsGuardedWith check₁ verify₁) :
+ (V₁.append V₂).run stmt (tr₁ ++ₜ tr₂) =
+ if check₁ stmt tr₁ then V₂.run (verify₁ stmt tr₁) tr₂ else failure
+```
+
+*Proof plan:* mirror `append_run_pure_left` (Composition.lean:311-319): `simp [Verifier.append_run,
+Verifier.run, hV₁]`, split the `if`; the `false` branch is `failure_bind`.
+
+### B4.3 Guarded append theorem (1–1.5 d; the core)
+
+```lean
+theorem Verifier.append_treeSpecialSound_of_guardedLeft
+ (hV₁ : V₁.IsGuardedWith check₁ verify₁)
+ (h₁ : V₁.treeSpecialSound init impl S₁ rel₁ rel₂)
+ (h₂ : V₂.treeSpecialSound init impl S₂ rel₂ rel₃) :
+ (V₁.append V₂).treeSpecialSound init impl (S₁.append S₂) rel₁ rel₃
+
+theorem Verifier.append_coordinateWiseSpecialSound_of_guardedLeft
+ (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) … -- same corollary shape as :414-428
+```
+
+*Proof plan* — transplant Composition.lean:366-407 with two deltas:
+1. Where the pure proof rewrites the composed run via `append_run_pure_left`, use
+ `append_run_guardedLeft` and case-split on `check₁`. In the `false` branch the composed run
+ is `failure`, so the leaf's acceptance (`Pr = 1`) contradicts `failure_not_accepting`
+ (`Pr = 0`, and `0 ≠ 1`) — the branch is vacuous. Every surviving leaf has `check₁ = true`
+ and the proof is *literally* the pure proof from there.
+2. Where the pure proof certifies each left-leaf output in `rel₂.language` via
+ `pure_accepting_of_mem`, use `guarded_accepting_of_mem` fed by the `check₁ = true` fact of
+ delta 1 (the tree machinery — `appendSplit`, `appendSplit_fst_isStructured`,
+ `appendSplit_sndAt_isStructured`, `appendSplit_fullTranscripts_append_of_mem` — is untouched).
+
+Watch: each left leaf needs *some* suffix transcript to learn `check₁ = true` from; that is the
+same nonemptiness the pure proof already extracts via `LeafPath.exists_of_mem_fullTranscripts`
+(used at Composition.lean:393). No new tree lemma expected.
+
+### B4.4 Guarded n-ary composition (0.5 d)
+
+```lean
+theorem Verifier.seqCompose_treeSpecialSound_of_guarded
+ (hV : ∀ i, (V i).IsGuarded) … -- otherwise verbatim SeqCompose.lean:364-386
+theorem Verifier.seqCompose_coordinateWiseSpecialSound_of_guarded …
+```
+
+*Proof plan:* same induction as SeqCompose.lean:364-386 (base `Verifier.id` is pure hence
+guarded; step uses B4.3 with `(hV 0).is_guarded`). Also the closure lemma the induction needs:
+
+```lean
+theorem Verifier.IsGuarded.append : V₁.IsGuarded → V₂.IsGuarded → (V₁.append V₂).IsGuarded
+-- composite check := fun s tr => check₁ s tr.fst && check₂ (verify₁ s tr.fst) tr.snd
+```
+
+(mirror of `IsPure.append`, [IsPure.lean:37](ArkLib/OracleReduction/Composition/Sequential/IsPure.lean#L37)).
+
+### B4.5 Guarded package and `▷ᵍ` (0.5 d)
+
+```lean
+structure GCWSSPackage … where -- CWSSPackage with isPure ↝ isGuarded
+ verifier … struct … relIn … relOut …
+ isGuarded : verifier.IsGuarded
+ isCWSS : …
+
+def CWSSPackage.toGuarded : CWSSPackage … → GCWSSPackage …
+def GCWSSPackage.append (L₁ L₂ : GCWSSPackage …) (hseam := by rfl) : GCWSSPackage …
+scoped infixr:65 " ▷ᵍ " => GCWSSPackage.append
+```
+
+`GCWSSPackage.append` mirrors Package.lean:79-97 with B4.3/B4.4's theorem and `IsGuarded.append`.
+This discharges the Hachi TODO's "guarded variant of `▷`" (Composition.lean:163).
+
+### B4 acceptance
+
+- `Guarded.lean` compiles sorry-free; `./scripts/validate.sh` green; existing files byte-identical.
+- A minimal `example`: a 1-message guarded verifier (`check := fun s tr => decide (tr 0 = s)`,
+ over `⟨!v[.P_to_V], !v[Nat]⟩`) `▷ᵍ`-composed with a pure identity package, its CWSS certificate
+ obtained via the no-challenge bridge + B4.3.
+- File-header note: oracle-level composition deferred (G4); `docs/wiki/repo-map.md` updated.
+
+---
+
+## 3. Milestone F2 — Eq. (20) → `R^lin` adapter (~3–4.5 d, incl. F2.0)
+
+**Goal.** A zero-round `ReduceClaim` package `rlinPackage` with
+`relIn = relOutE (QuadEval, escape-threaded)` and `relOut = relRlinE`, `▷`-appended onto the
+(escape-threaded) finished chain. New directory
+`ArkLib/Commitments/Functional/Hachi/LinSumcheck/` (F2, F4, and later F5–F9 live here).
+
+### F2.0 Escape threading (1–1.5 d) — `LinSumcheck/Escape.lean`
+
+Per G1. Deliverables:
+
+1. `Set.withEscape` (+ 3 simp lemmas: `mem_withEscape_inl/inr`, `withEscape_language`), placed
+ in the CWSS folder (it is protocol-agnostic): new small file
+ `CoordinateWiseSpecialSoundness/Escape.lean`, or the top of `LinSumcheck/Escape.lean` if
+ maintainers prefer zero framework surface — decide at PR time, default the former.
+2. Threaded ReduceClaim: **no new framework lemma** — instantiate the existing
+ `ReduceClaim.verifier_coordinateWiseSpecialSound`
+ ([ReduceClaim.lean:186](ArkLib/ProofSystem/Component/ReduceClaim.lean#L186)) at witness types
+ `WitIn ⊕ E` / `WitOut ⊕ E` with `mapWitInv' := Sum.map (mapWitInv s) id` and the case-split
+ `hRel`.
+3. Threaded QuadEval, in `LinSumcheck/Escape.lean` (all *new* declarations):
+
+```lean
+def relInE (esc : Set E) := (relIn Φ base βSq γ κ).withEscape esc -- witness: QuadEvalWitness ⊕ E
+def relOutE (esc : Set E) := (relOut Φ base ω γ).withEscape esc -- witness: QuadEvalResponse ⊕ E
+
+noncomputable def buildWitnessE … : -- branch responses now `QuadEvalResponse ⊕ E`
+ (Fin (2^r + 1) → QuadEvalResponse … ⊕ E) → … → QuadEvalWitness … ⊕ E
+-- if ∃ j, resp j = .inr e (pick least j): output .inr e; else delegate to buildWitness
+
+theorem buildWitnessE_mem_relInE … -- hmk: escape branch = pass-through (relOutE gives e ∈ esc);
+ -- all-inl branch = `buildWitness_mem_relIn` verbatim
+theorem quadEval_coordinateWiseSpecialSound_withEscape … -- via coordinateWiseSpecialSound_of_mkWitness
+def quadEvalPackageE … : CWSSPackage … ; def bridgePackageE … ; def evalChainE := bridgePackageE ▷ quadEvalPackageE
+```
+
+*Proof plan for the hmk:* case-split on `∃ j, (resp j).isRight`. Escape case: the chosen
+branch's `relOutE`-membership is exactly `e ∈ esc`, and `relInE`'s `.inr` case is the same
+`e ∈ esc` — done. All-`inl` case: strip the `Sum.inl`s and apply `buildWitness_mem_relIn`
+unchanged. `Nonempty (QuadEvalWitness … ⊕ E)` from the existing `Nonempty` instance via `.inl`.
+
+*Faithfulness note:* `relInE/relOutE` at `E := Empty` are equivalent to `relIn/relOut` — state
+this as two one-line lemmas so nothing is lost.
+
+### F2.1 Block-vector/matrix helpers (0.5–1 d) — extend `ArkLib/Data/Lattices/Vectors.lean`
+
+All generic over `[NonUnitalNonAssocSemiring P]` (or whatever `dot` currently assumes):
+
+```lean
+def PolyVec.finAppend (u : PolyVec P a) (v : PolyVec P b) : PolyVec P (a + b) := Fin.append u v
+def PolyMatrix.stackRows (M₁ : PolyMatrix P n₁ c) (M₂ : PolyMatrix P n₂ c) : PolyMatrix P (n₁+n₂) c
+ -- Fin.addCases on the row index
+def PolyMatrix.pasteCols (M₁ : PolyMatrix P n c₁) (M₂ : PolyMatrix P n c₂) : PolyMatrix P n (c₁+c₂)
+def vecMatMul (u : PolyVec P n) (M : PolyMatrix P n c) : PolyVec P c -- row-vector · matrix
+
+theorem dot_finAppend : dot (finAppend u₁ u₂) (finAppend v₁ v₂) = dot u₁ v₁ + dot u₂ v₂
+theorem matVecMul_stackRows : (stackRows M₁ M₂) *ᵥ v = finAppend (M₁ *ᵥ v) (M₂ *ᵥ v)
+theorem matVecMul_pasteCols : (pasteCols M₁ M₂) *ᵥ (finAppend v₁ v₂) = M₁ *ᵥ v₁ + M₂ *ᵥ v₂
+theorem dot_matVecMul : dot u (M *ᵥ v) = dot (vecMatMul u M) v -- splitForm associativity
+```
+
+`dot_finAppend` reduces via `dot_eq_sum` (Vectors.lean:112) + `Fin.sum_univ_add`. Also the norm
+splitter in `NormBounds/Basic.lean`:
+
+```lean
+theorem vecLInftyNorm_finAppend :
+ vecLInftyNorm Φ (finAppend u v) = max (vecLInftyNorm Φ u) (vecLInftyNorm Φ v)
+```
+
+and two rewriting lemmas in `QuadEval/Gadgets.lean`'s namespace (new file
+`LinSumcheck/Rows.lean` if maintainers prefer not to touch Gadgets.lean):
+
+```lean
+theorem tensorG1_eq_dot_vecMatMul : tensorG1 Φ base δ c x = dot (vecMatMul c (gadgetMatrix …)) x
+theorem tensorG_eq_matVecMul_flattenBlocks :
+ tensorG Φ base k δ c x = (tensorGMatrix Φ base k δ c) *ᵥ PolyVec.flattenBlocks x
+ -- tensorGMatrix := the k × (blocks·k·δ) block-row [c₁·G | … | c_{2^r}·G], defined via finProdFinEquiv
+```
+
+**Convention pin (v2's A1/R4, scoped down):** one `example` block with `decide` fixing the
+`Fin.addCases` orientation of `stackRows`/`finAppend` and the `finProdFinEquiv` block order of
+`flattenBlocks` at a `2×2` toy instance. Do this *first*; never resolve an index cast mid-proof.
+
+### F2.2 `RlinStatement`, `relRlin(E)`, the adapter (1.5–2 d) — `LinSumcheck/Rlin.lean`
+
+Column layout of the stacked witness `ζ := ŵ ++ flatten t̂ ++ ẑ`, row layout c1–c5:
+
+```
+μ := (2^r · messageDigits) + (2^r · (innerRows · innerDigits)) + ((2^m · messageDigits) · zDigits)
+n := dRows + (outerRows + (1 + (1 + innerRows))) -- fix associativity once, in this order
+
+ ŵ flatten t̂ ẑ rhs
+c1 [ D | 0 | 0 ] = v
+c2 [ 0 | B | 0 ] = u
+c3 [ (bᵀG_{2^r,δ}) row | 0 | 0 ] = y
+c4 [ (cᵀ⊗G₁) row | 0 | −(aᵀG_{2^m}J) ] = 0
+c5 [ 0 | tensorGMatrix c | −(A·J) ] = 0
+```
+
+```lean
+structure RlinStatement (Φ) (n μ : ℕ) where
+ M : PolyMatrix (Rq Φ) n μ
+ yvec : PolyVec (Rq Φ) n
+ bound : ℕ
+
+def relRlin : Set (RlinStatement Φ n μ × PolyVec (Rq Φ) μ) :=
+ {p | p.1.M *ᵥ p.2 = p.1.yvec ∧ vecLInftyNorm Φ p.2 ≤ p.1.bound}
+def relRlinE (esc : Set E) := relRlin.withEscape esc
+
+def rlinStmt (X : QuadEvalStatement … × CarrierCom Φ dRows × (Fin (2^r) → ShortChallenge Φ ω)) :
+ RlinStatement Φ n μ -- assemble via stackRows/pasteCols; bound := γ
+def unstack : PolyVec (Rq Φ) μ → QuadEvalResponse … -- Fin.addCases splits + finProdFinEquiv un-flatten
+```
+
+Key lemma (state as an **iff** — the `→` direction is F2's pull-back, the `←` direction is the
+honest-prover side needed later):
+
+```lean
+theorem mem_relRlin_iff_mem_relOut :
+ (rlinStmt X, ζ) ∈ relRlin ↔ (X, unstack ζ) ∈ relOut Φ base ω γ
+```
+
+*Proof plan:* `matVecMul_stackRows` + `matVecMul_pasteCols` split `Mζ = yvec` into five
+`finAppend`-component equations; c3/c4 via `dot_matVecMul`/`tensorG1_eq_dot_vecMatMul`; c5 via
+`tensorG_eq_matVecMul_flattenBlocks` + `matVecMul_matMul` (for `A·J`); move `−` blocks across
+(`sub_eq_zero`); the norm conjunct by `vecLInftyNorm_finAppend` (`max ≤ γ ↔` three `≤ γ`);
+`unstack ∘ stack = id` component lemmas from the F2.1 convention pins. This is pure index
+bookkeeping — the budgeted risk item (R-F2 below).
+
+Package and composition:
+
+```lean
+def rlinPackage … : CWSSPackage init impl
+ (QuadEvalStatement … × CarrierCom … × (Fin (2^r) → ShortChallenge …)) (QuadEvalResponse … ⊕ E)
+ (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ ⊕ E) !p[] :=
+ -- ReduceClaim.verifier (mapStmt := rlinStmt); struct := CWSSStructure.ofIsEmpty
+ -- isCWSS via ReduceClaim.verifier_coordinateWiseSpecialSound,
+ -- hRel := Sum-case-split: .inl from (mem_relRlin_iff_mem_relOut).mp; .inr pass-through
+def evalRlinChain := evalChainE ▷ rlinPackage
+theorem evalRlin_coordinateWiseSpecialSound := evalRlinChain.isCWSS
+```
+
+### F2 acceptance
+
+- `Escape.lean`, `Rlin.lean` (+ `Rows.lean`, Vectors additions) compile sorry-free;
+ `evalRlin_coordinateWiseSpecialSound` end-to-end from `relPolyEvalE` to `relRlinE`.
+- The `E := Empty` degeneration lemmas compile.
+- `decide` convention examples compile; validate.sh green; repo-map entry for `LinSumcheck/`.
+
+---
+
+## 4. Milestone F3 — quotient-lift algebra (~2–3 d)
+
+**Goal.** The generic `Rq ↔ (ZMod q)[X] ↔ F` bridge Lemma 9 consumes. **New file**
+`ArkLib/Data/Lattices/CyclotomicRing/QuotientLift.lean` (generic over `R` where possible; no
+Hachi imports — this is reusable, LatticeFold-adjacent material). Everything below is stated
+against `Polynomial R` via `(a : Rq Φ).1.toPoly`; write the one-line abbreviation
+`Rq.rep (a : Rq Φ) : Polynomial R := a.1.toPoly` first and use it throughout.
+
+### F3.1 Scalar quotient-witness lemma (0.5–1 d)
+
+```lean
+-- d := Φ.φ.toPoly.natDegree (= 2^α for powTwoCyclotomic); hypothesis hd : 2 ≤ d
+theorem exists_quotient_witness_of_quotient_eq
+ (hS : S.natDegree ≤ 2*d - 2) (hy : y.natDegree < d)
+ (h : Ideal.Quotient.mk Φ.modIdeal S = Ideal.Quotient.mk Φ.modIdeal y) :
+ ∃ ρ : Polynomial R, ρ.natDegree ≤ d - 2 ∧ S = y + Φ.φ.toPoly * ρ
+
+theorem quotient_eq_of_eq_add_mul -- the trivial converse: apply mk, mk φ = 0
+```
+
+*Proof plan:* `Ideal.Quotient.eq` + `Ideal.mem_span_singleton` give `Φ.φ.toPoly ∣ (S − y)`
+(exactly the step inside `Rq.toQuotient_injective`, Rq.lean:107-121 — imitate, don't reuse, its
+proof body); set `ρ := (S − y) /ₘ Φ.φ.toPoly`; the identity from `Polynomial.modByMonic_add_div`
++ `(Polynomial.modByMonic_eq_zero_iff_dvd hmonic).mpr`; the degree bound from
+`Polynomial.natDegree_divByMonic` and `natDegree (S − y) ≤ 2d − 2` (max of `hS`, `hy`).
+Monicity: the `IsCyclotomic` field. Mind ℕ-subtraction: `hd : 2 ≤ d` keeps `d - 2`, `2*d - 2`
+well-behaved; add the `powTwoCyclotomic` corollary with `hα : 1 ≤ α` discharging `hd`.
+
+### F3.2 Row form over `Rq` (0.5 d)
+
+```lean
+theorem Rq.dot_eq_iff_exists_quotient (Mrow z : PolyVec (Rq Φ) μ) (y : Rq Φ) :
+ dot Mrow z = y ↔
+ ∃ ρ : Polynomial R, ρ.natDegree ≤ d - 2 ∧
+ (∑ j, (Mrow j).rep * (z j).rep) = y.rep + Φ.φ.toPoly * ρ
+```
+
+*Proof plan:* `dot_eq_sum`; equality in `Rq` ↔ equality of `equivQuotient` images
+(`RingEquiv.injective`) ↔ `mk (∑ reps·reps) = mk y.rep` (push `mk`/`toPoly` through sum and
+product: `map_sum`, `map_mul`, `quotientHom_apply`); then F3.1 with
+`hS := natDegree_sum_le + natDegree_mul_le + natDegree_val_toPoly_lt` (each `rep` has
+`natDegree < d`) and `hy := natDegree_val_toPoly_lt`. Matrix corollary
+`Rq.matVecMul_eq_iff_exists_quotient` (row-indexed `ρ : Fin n → Polynomial R`) by
+`funext`-style row aggregation + `Classical.choice`/`Finset` packaging.
+
+### F3.3 Evaluation and interpolation (1 d)
+
+```lean
+variable {F : Type} [Field F] (φF : ZMod q →+* F) -- injective: RingHom.injective
+
+abbrev evalAt (a : F) : Polynomial (ZMod q) →+* F := Polynomial.eval₂RingHom φF a
+
+-- completeness direction: ring-hom push-through, `map_sum/map_mul/map_add`
+theorem evalAt_row_eq_of_lift (h : S = y + φ * ρ) (a : F) :
+ evalAt φF a S = evalAt φF a y + evalAt φF a φ * evalAt φF a ρ
+
+-- soundness kernel (the Lemma 9 engine):
+theorem lift_eq_of_eval_eq_at_distinct
+ (hdeg : (S - y - φ * ρ).natDegree < N) (A : Fin N ↪ F)
+ (h : ∀ i, evalAt φF (A i) S = evalAt φF (A i) y + evalAt φF (A i) φ * evalAt φF (A i) ρ) :
+ S = y + φ * ρ
+```
+
+*Proof plan:* let `defect := Polynomial.map φF (S − y − φ*ρ)`; `eval₂ = eval ∘ map`
+(`Polynomial.eval₂_eq_eval_map`), so `h` says `defect.eval (A i) = 0`;
+`natDegree defect ≤ natDegree (S − y − φρ) < N` (`Polynomial.natDegree_map_le`); Mathlib's
+`Polynomial.eq_zero_of_natDegree_lt_card_of_eval_eq_zero` (Roots.lean:690) with the embedding
+`A` kills `defect`; `Polynomial.map_injective φF (RingHom.injective φF)` transfers `= 0` back;
+`sub_eq_zero`. Degree arithmetic for the intended use: `S` at `≤ 2d−2`, `φρ` at `≤ d + (d−2)`,
+so `hdeg` holds with `N := 2*d` — record this as the packaged corollary:
+
+```lean
+theorem Rq.dot_eq_of_eval_rows_at_distinct -- 2d distinct α's + per-α row equations
+ (hρdeg : ρ.natDegree ≤ d - 2) … : dot Mrow z = y -- composes F3.3 + F3.2 (←)
+```
+
+### F3.4 (thin, optional — may slide to F5) digit decomposition of ρ (0.5 d)
+
+`ρ = ∑ u, (b^u : ZMod q) • ρdig u` with per-digit coefficient bounds, as a `Polynomial`-level
+wrapper over `zmodDigitDecomposition` (Gadget/Basic.lean:113) applied coefficient-wise
+(`Polynomial.ofFinsupp`/`∑ k, C (digit …) * X^k` over `Finset.range d`). Only F5's `w̃`-table
+needs it; F4 carries `ρ` whole. Implement only if time permits inside F3's budget.
+
+### F3 acceptance
+
+- `QuotientLift.lean` sorry-free, no Hachi imports, validate.sh green.
+- A `powTwoCyclotomic`-instantiated `example` at `q = 5, α = 1` (degree-2 ring) checking F3.1's
+ statement shape by `decide`/`native_decide` on a concrete instance, plus one `example`
+ instantiating F3.3 at `F := ZMod 5`, `φF := RingHom.id` (sanity: the abstraction admits the
+ base field itself).
+
+---
+
+## 5. Milestone F4 — HMZ25 lift, Fig. 4 / Lemma 9 (~4–5.5 d)
+
+**Goal.** The two-round reduction (`t = Com(w̃)` then `α ← F`), CWSS at `k = 2d`, output
+relation `relLiftE`, packaged and `▷`-appended onto `evalRlinChain`.
+
+### F4.1 Scalar single-round CWSS lemma (1.5–2 d) — generic framework
+
+**New file** `ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean`,
+the `(ℓ = 1, k)` twin of `SingleRound.lean` (which stays untouched at `(2^r, 2)`):
+
+```lean
+@[reducible] def pSpecScalar (Msg C : Type) : ProtocolSpec 2 := ⟨!v[.P_to_V, .V_to_P], !v[Msg, C]⟩
+
+@[reducible] def scalarStructure (k : ℕ) (hk : 2 ≤ k) : CWSSStructure (pSpecScalar Msg C) :=
+ CWSSStructure.ofSpecialSound (fun _ => k) (fun _ => hk) -- arity k
+
+theorem coordinateWiseSpecialSound_of_mkWitness_scalar
+ (V : Verifier oSpec StmtIn (StmtIn × Msg × C) (pSpecScalar Msg C))
+ (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0,rfl⟩, tr.challenges ⟨1,rfl⟩))
+ (relIn : Set (StmtIn × WitIn)) (relOut : Set ((StmtIn × Msg × C) × WitOut)) [Nonempty WitOut]
+ (mkWitness : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → WitIn)
+ (hmk : ∀ s v (fam : Fin k → C) resp,
+ (∀ j, ((s, v, fam j), resp j) ∈ relOut) → Function.Injective fam →
+ (s, mkWitness s v fam resp) ∈ relIn) :
+ V.coordinateWiseSpecialSound init impl (scalarStructure k hk) relIn relOut
+```
+
+*Proof plan:* transplant SingleRound.lean:345-410. The tree at arity `k` has one message node
+and one challenge node with `k` children; `readPre`/`readChallenges`/`tree_shape` re-derive with
+`Fin.cast` along `scalarStructure`'s `arity = 1*(k−1)+1 = k` (one `Nat` simp lemma). The star
+machinery collapses: `nodeOk` at `ℓ = 1` is injectivity by `isSpecialSoundFamily_one_iff_injective`
+(Basic.lean:111) composed with the `Equiv.funUnique` decomposition — so `hmk` receives plain
+`Function.Injective fam` instead of `StarAt`. Branch acceptance → `relOut`-membership via the
+same `branch_relOut_language` pattern (uses `mem_of_pure_accepting`). This lemma is also the
+substrate for F6/F7's rounds later — build it clean.
+
+### F4.2 Witness, commitment abstraction, relations (1–1.5 d) — `LinSumcheck/Lift.lean`
+
+```lean
+/-- Eq. (21)'s committed data in polynomial form: the R^lin witness plus the per-row
+quotient polynomials with their structural degree bound. (Digit form arrives in F5.) -/
+structure LiftedWitness (Φ) (μ n : ℕ) where
+ z : PolyVec (Rq Φ) μ
+ ρ : Fin n → Polynomial (ZMod q)
+ hρ : ∀ i, (ρ i).natDegree ≤ d - 2
+
+/-- Abstract binding commitment for `w̃` (G2: instantiated later; Lemma 9 needs only binding). -/
+structure LiftCom (W E : Type) where
+ TCom : Type
+ com : W → TCom
+ esc : Set E
+ escOfCollision : W → W → E
+ collision_mem : ∀ w w', w ≠ w' → com w = com w' →
+ Short w → Short w' → escOfCollision w w' ∈ esc
+ -- `Short` = the relLift range predicate below, threaded as a parameter of the structure
+ -- (weak binding is norm-conditioned — Lemma 7 / `outputToModuleSIS_valid_of_verified` pattern)
+
+variable (K : LiftCom (LiftedWitness Φ μ n) E) (φF : ZMod q →+* F)
+
+def LiftStatement := RlinStatement Φ n μ × K.TCom × F -- pass-through shape
+
+def relLift : Set (LiftStatement × LiftedWitness Φ μ n) :=
+ {p | let ((s, t, a), w) := p
+ K.com w = t ∧
+ (∀ i, evalAt φF a (rowSum s.M w.z i) =
+ evalAt φF a ((s.yvec i).rep) + evalAt φF a Φ.φ.toPoly * evalAt φF a (w.ρ i)) ∧
+ vecLInftyNorm Φ w.z ≤ s.bound ∧ RhoShort w.ρ}
+def relLiftE := relLift.withEscape K.esc
+```
+
+where `rowSum s.M w.z i := ∑ j, (s.M i j).rep * (w.z j).rep` (definition shared with F3.2) and
+`RhoShort` is the coefficient-range predicate on `ρ` (bounded by the digit range; exact constant
+pinned here, feeding F5). Decision recorded in the file header: `LiftCom` stays **abstract**
+in F4; the concrete inner-outer instantiation (paper §4.5 "commit without re-decomposition") is
+a Phase-G/F5 deliverable, and `collision_mem` is exactly the obligation
+`outputToModuleSIS_valid_of_verified` will discharge there.
+
+### F4.3 pSpec, prover, verifier (0.5 d)
+
+```lean
+-- pSpec := pSpecScalar K.TCom F ; instances by the ⟨0,h⟩/⟨1,_⟩ matching idiom;
+-- SampleableType F is a section hypothesis (G5)
+def liftVerifier : Verifier oSpec (RlinStatement Φ n μ) (LiftStatement …) (pSpecScalar K.TCom F) where
+ verify := fun stmt tr => pure (stmt, tr.messages ⟨0,rfl⟩, tr.challenges ⟨1,rfl⟩)
+def liftProver (computeW : …) : Prover … -- QuadEval Reduction.lean:265-298 skeleton, honest w̃
+```
+
+### F4.4 Extraction — Lemma 9 (1.5–2 d)
+
+```lean
+noncomputable def liftBuildWitness
+ (s : RlinStatement Φ n μ) (t : K.TCom) (fam : Fin (2*d) → F)
+ (resp : Fin (2*d) → LiftedWitness Φ μ n ⊕ E) : PolyVec (Rq Φ) μ ⊕ E
+-- (a) some branch is .inr e → .inr e
+-- (b) two branches carry w ≠ w' → .inr (K.escOfCollision w w')
+-- (c) all branches carry the same w → .inl w.z
+
+theorem liftBuildWitness_mem_relRlinE
+ (hd : 2 ≤ d) (hresp : ∀ j, ((s, t, fam j), resp j) ∈ relLiftE) (hinj : Function.Injective fam) :
+ (s, liftBuildWitness …) ∈ relRlinE K.esc
+```
+
+*Proof plan* (the paper's Lemma 9, case-faithful):
+- (a): pass-through, as in F2.0.
+- (b): both branches' `relLift` give `K.com w = t = K.com w'` and both `Short`;
+ `K.collision_mem` puts the escape in `K.esc` — `relRlinE`'s `.inr` case. (This is Remark 2's
+ weak-binding route.)
+- (c): the shared `w` satisfies, for each row `i`, the `evalAt`-equation at all `2d` **distinct**
+ (injective `fam`) points; `(rowSum − yvec.rep − φ·ρ i).natDegree ≤ 2d − 2 < 2d` from `w.hρ` +
+ representative degree bounds; `lift_eq_of_eval_eq_at_distinct` (F3.3, `N := 2d`) yields the
+ `(ZMod q)[X]`-identity per row; `Rq.dot_eq_iff_exists_quotient` (F3.2, `←` direction — or
+ directly the packaged `Rq.dot_eq_of_eval_rows_at_distinct`) yields `s.M *ᵥ w.z = s.yvec`;
+ the norm conjunct of `relLift` is already `vecLInftyNorm w.z ≤ s.bound`. Both `relRlin`
+ conjuncts hold — `.inl` case.
+
+Then:
+
+```lean
+theorem lift_coordinateWiseSpecialSound … :
+ liftVerifier.coordinateWiseSpecialSound init impl (scalarStructure (2*d) (by omega))
+ (relRlinE K.esc) (relLiftE …)
+ -- coordinateWiseSpecialSound_of_mkWitness_scalar with mkWitness := liftBuildWitness,
+ -- hmk := liftBuildWitness_mem_relRlinE
+```
+
+Note `2 ≤ 2*d` from `hd`; no field-size hypothesis is needed for CWSS (an injective
+`Fin (2*d) ↪ F` family is the *tree's* problem — knowledge error, out of scope, only needs
+`2d ≤ |F|` which the eventual instantiation satisfies).
+
+### F4.5 Package and chain (0.5 d)
+
+```lean
+def liftPackage … : CWSSPackage init impl (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ ⊕ E)
+ (LiftStatement …) (LiftedWitness Φ μ n ⊕ E) (pSpecScalar K.TCom F)
+def evalLiftChain := evalRlinChain ▷ liftPackage -- seam relRlinE, by rfl (G6)
+theorem evalLift_coordinateWiseSpecialSound := evalLiftChain.isCWSS
+```
+
+`evalLift_coordinateWiseSpecialSound` is this plan's end-to-end deliverable: CWSS from
+`relPolyEvalE` all the way to `relLiftE` — i.e. paper Figures 3 + 4 (Lemmas 8 + 9) composed,
+escape-threaded, ready for F5/F6 to consume `relLiftE` as their `relIn`.
+
+### F4 acceptance
+
+- `ScalarRound.lean` and `Lift.lean` sorry-free; `evalLift_coordinateWiseSpecialSound` compiles;
+ validate.sh green (`--lint` before PR).
+- Prover skeleton `liftProver` compiles (completeness stays a skeleton, D12).
+- An `example` instantiating `F := ZMod q`, `φF := RingHom.id` and a trivial `LiftCom`
+ (`TCom := LiftedWitness …`, `com := id`, `esc := ∅` unreachable since `com` injective) —
+ proving the abstraction is inhabitable without any Phase-B–E material.
+- File-header faithfulness notes: (i) Fig. 4 sends `(z, r)` in the clear — here they are the
+ never-sent output witness (v2's D6, QuadEval precedent); (ii) `Com` abstract pending the
+ §4.5 inner-outer instantiation.
+
+---
+
+## 6. The sub-chain after these four milestones
+
+| # | Stage | pSpec | Verifier | CWSS structure | relIn → relOut | Status after this plan |
+|---|---|---|---|---|---|---|
+| 1 | bridge (threaded) | `!p[]` | pure | `ofIsEmpty` | `relPolyEvalE → relInE` | F2.0 |
+| 2 | QuadEval (threaded) | msg + vector chal | pure | `foldStructure` | `relInE → relOutE` | F2.0 |
+| 3 | R^lin adapter | `!p[]` | pure | `ofIsEmpty` | `relOutE → relRlinE` | F2 |
+| 4 | HMZ25 lift | `t` + scalar `α` | pure | `ofSpecialSound k = 2d` | `relRlinE → relLiftE` | F4 |
+
+All four verifiers are pure — **B4 is not consumed inside this plan's chain**; it is the
+groundwork the *next* milestones (F7 sumcheck loop, F8 final eval, G3 handoff) compose with,
+built first per the agreed ordering while its design is fresh from the framework recon.
+Escape budget `E` is a single parameter threaded through rows 1–4, instantiated at `K.esc`.
+
+## 7. Effort summary and sequencing
+
+| Milestone | Estimate | Hard prerequisites |
+|---|---|---|
+| B4 | 2.5–3.5 d | maintainer ping (R3) |
+| F2 (incl. F2.0) | 3–4.5 d | none |
+| F3 | 2–3 d | none (parallelizable with F2 if desired; sequential per instruction) |
+| F4 | 4–5.5 d | F2 (seam), F3 (F3.2/F3.3), F4.1 |
+| **Total** | **~12–16.5 d** | |
+
+## 8. Risks
+
+- **R-B4-a (VCVio probability plumbing).** `failure_not_accepting` needs the right
+ `simulateQ`/`OptionT` simp set; the pure proofs (Composition.lean:333-362, SeqCompose.lean:70-82)
+ are the map. Budgeted inside B4.1; if it fights back, extract the acceptance-probability facts
+ as standalone `OracleComp` lemmas and ask maintainers where they belong.
+- **R-B4-b (framework churn).** Same mitigation as v2's R3: everything in new files, existing
+ theorems byte-identical, PR flagged as security-infrastructure.
+- **R-F2-a (index bookkeeping).** The block-matrix equivalence is the milestone's real cost.
+ Mitigation: F2.1's `decide` convention pins *first*; every cast through `Fin.addCases`/
+ `finProdFinEquiv` gets its own simp lemma; treat any mid-proof `Fin.cast` as a red flag (v2 R4).
+- **R-F2-b (escape-threading reception).** The `withEscape` design changes no existing
+ declaration but *does* add a parallel chain (`evalChainE`). Alternative if maintainers object:
+ make `E` an argument of the original relations with `E := Empty` as the old theorems — more
+ invasive; keep as fallback only.
+- **R-F3-a (CPolynomial ↔ Polynomial friction).** All identities are stated over
+ `Polynomial R` via `.rep`; the only CompPoly surface is `toPoly` of ring operations
+ (`ringEquiv` handles it). Half-day slack budgeted.
+- **R-F4-a (arity-`k` re-derivation).** `ScalarRound.lean` re-derives SingleRound's tree readers
+ at arity `k`; the `(2^r, 2)` proofs are the template but `Fin.cast` normalization along
+ `1*(k−1)+1 = k` needs one careful simp lemma. Budgeted in F4.1.
+- **R-F4-b (commitment abstraction).** If F5 later needs `TCom` data F4 hid (e.g. homomorphic
+ structure for the avoid-re-decomposition trick), `LiftCom` grows fields — additive, not
+ breaking. The abstract-now/instantiate-later split is deliberate (G2).
+
+## 9. Documentation obligations (same PRs as the code — CLAUDE.md guardrail)
+
+- `docs/wiki/repo-map.md`: `CoordinateWiseSpecialSoundness/{Guarded,Escape,ScalarRound}.lean`,
+ `Hachi/LinSumcheck/`, `CyclotomicRing/QuotientLift.lean`.
+- `docs/kb/papers/NOZ26.md`: mark Lemma 9 formalized; record the escape-threading design and
+ the `LiftCom` abstraction decision.
+- Blueprint: `blueprint/src/proof_systems/hachi_sumcheck.tex` stub covering Fig. 4 / Lemma 9
+ (F6–F9 will extend it).
+- Update `HACHI_RING_SWITCHING_PLAN.md` §4's F2/F3/F4 entries with a pointer to this file
+ (anchors there are stale; this file is authoritative for the four milestones).
diff --git a/docs/skills/README.md b/docs/skills/README.md
index 759a49c876..1cd3ddaaea 100644
--- a/docs/skills/README.md
+++ b/docs/skills/README.md
@@ -21,6 +21,8 @@ After using a skill, review whether it should be updated:
- [`discharge-lemmas.md`](discharge-lemmas.md) - workflow for triaging, placing, stating, and
proving `sorry`s and open proof obligations, then summarizing what is proved vs. deferred.
+- [`prove-milestone.md`](prove-milestone.md) - four-stage, paper-audited workflow for freezing and
+ constructively proving one Hachi milestone, then improving the workflow from run evidence.
- [`fix-lean-warnings.md`](fix-lean-warnings.md) - workflow for cleaning Lean 4 linter and style
warnings safely and incrementally.
- [`make-pr-ready.md`](make-pr-ready.md) - checklist to get a branch PR-ready: follow the
diff --git a/docs/skills/prove-milestone.md b/docs/skills/prove-milestone.md
new file mode 100644
index 0000000000..89baadaa95
--- /dev/null
+++ b/docs/skills/prove-milestone.md
@@ -0,0 +1,426 @@
+# /prove-milestone
+
+Use this workflow to discharge one Hachi milestone named by a row, subprotocol, milestone code, or
+file in
+[`hachi-overview.html`](../../ArkLib/Commitments/Functional/Hachi/hachi-overview.html). The goal is
+not merely to make Lean accept existing statements. First establish that the statements encode the
+intended protocol and theorem; then make every proof obligation tractable; only then fill the proof
+bodies without changing what they mean.
+
+This workflow is deliberately stricter than [`discharge-lemmas.md`](discharge-lemmas.md). A Hachi
+milestone is complete only when its paper contract, definitions, public theorem, package, and
+composition seam agree and its entire proof-obligation closure is constructive and `sorry`-free.
+
+## Invocation contract
+
+Require an unambiguous target such as “row 8 / paired sumcheck rounds”, “F7”, or
+`Sumcheck/Rounds.lean`. If the requested name selects several dashboard entries, resolve the
+smallest semantic subprotocol that has its own input relation, output relation, verifier, and CWSS
+certificate; ask only if two materially different scopes remain.
+
+Treat the dashboard as an index and status report, not as the specification. Establish the target
+from all of these sources:
+
+- the exact version of the primary paper cited by the code, especially the referenced definition,
+ figure, equations, lemma, bounds, and surrounding qualifications;
+- the target Lean files and their imports, docstrings, exported package, and consumers;
+- the seam table and sorry provenance in `Hachi/Composition.lean`;
+- `docs/kb/papers/NOZ26.md`, relevant `docs/kb/audits/`, and applicable `HACHI_*.md` design notes;
+- the generic ArkLib definitions of the claimed security notion and composition operator.
+
+The paper is primary. Repository notes explain intent and known deviations but cannot establish
+paper faithfulness by themselves. Record the paper version and page/figure/equation references. If
+the primary source is unavailable, do not declare Stage 1 complete.
+
+## Global invariants
+
+Maintain these invariants throughout the task:
+
+1. Preserve unrelated and pre-existing work. Start with `git status --short`, record the initial
+ diff and untracked files in scope, and never reset, overwrite, or silently absorb user changes.
+2. Keep a target-closure ledger. Include the milestone's semantic definitions, proof declarations,
+ helper lemmas, exported package, and the immediate composition seam. Do not count unrelated
+ Hachi sorries as part of the target, but do expose any target theorem that transitively depends
+ on them.
+3. Use the exact primary-source claim as the comparison point. Label every deliberate deviation as
+ a strengthening, weakening, repair, generalization, or implementation-only representation
+ choice, and justify the direction needed for soundness and completeness.
+4. Never make a theorem easier by silently strengthening an input relation, weakening an output
+ relation, shrinking the challenge space, adding a false/unmotivated hypothesis, or moving a
+ verifier check into an assumption.
+5. Make the remaining `sorry` count in the target closure monotonically decrease after the Stage 2
+ freeze. Do not move, rename, hide, or replace a gap with another trust mechanism.
+6. Do not use `native_decide`, `classical`, any `Classical.*` declaration, `axiom`, `admit`,
+ `Lean.ofReduceBool`, an unsafe/opaque proof surrogate, or a theorem whose transitive axiom set
+ contains `sorryAx`. Do not introduce project axioms. Treat `Classical.choice` as forbidden even
+ when it enters indirectly through a reused theorem. Record unavoidable foundational use of
+ `propext` or `Quot.sound`; do not call a result “axiom-free” if either appears.
+7. Use a fresh independent reviewer when subagents are available. Give reviewers the raw paper
+ section, Lean files, and current artifacts, not the intended verdict or a proposed fix.
+
+Keep scratch manifests and experiments under `/tmp`, not as new root-level planning files. Promote
+new, durable paper findings to `docs/kb/` when they materially change ArkLib's understanding of the
+paper.
+
+## Required artifacts
+
+Maintain these artifacts during the run, in the conversation or in temporary files:
+
+- **scope manifest** — target, source files, declarations, imports, consumers, and initial dirty
+ state;
+- **milestone contract** — paper-to-Lean comparison with exact source references;
+- **audit ledger** — findings, severity, resolution, and evidence;
+- **proof DAG** — every `sorry`, its dependencies, plan, difficulty, and status;
+- **semantic-freeze manifest** — the Stage 2 definitions, declaration signatures, attributes,
+ instances, imports, package wiring, and allowed proof-body locations;
+- **verification log** — per-proof builds, axiom output, scoped searches, final build, and semantic
+ review.
+- **skill-improvement log** — end-of-run evidence, candidate edits, decisions, applied changes, and
+ validation of the revised skill.
+
+Do not use a prose plan as a substitute for typechecking. Each stage has an explicit exit gate.
+
+## Stage 1 — establish the right formalization
+
+This stage may change definitions, theorem statements, abstractions, hypotheses, and file
+boundaries. It must end with the intended API fully stated and typechecking, with intentional proof
+holes marking all remaining work.
+
+### 1. Reconstruct the paper contract independently
+
+Before accepting the current Lean design, write a compact contract covering:
+
+| Contract item | Questions to answer |
+| --- | --- |
+| Claim | What exact correctness, soundness, CWSS, or reduction statement is claimed? |
+| Data | What are the public statement, witness, messages, challenges, responses, and commitment data? |
+| Protocol | What is sent or sampled in each round, from which space and distribution, and in what order? |
+| Acceptance | Which checks run at runtime, and which facts belong to the input or output relation? |
+| Extraction | What accepting transcript shape is assumed, what witness/escape is returned, and with what arity/error? |
+| Parameters | What degree, norm, cardinality, non-emptiness, distinctness, and field/ring assumptions are required? |
+| Composition | What exact relation enters this seam, what relation leaves it, and what data is retained or dropped? |
+
+Translate every row into the corresponding Lean declaration or mark it missing. For relation
+changes, state an equality, equivalence, or containment and verify that its direction is the one the
+security argument needs. “Looks analogous” is not evidence.
+
+### 2. Run an adversarial formalization audit
+
+Audit from several angles:
+
+- **Faithfulness:** compare quantifier order, indices, dimensions, challenge type and distribution,
+ transcript shape, verifier checks, bounds, exceptional/escape cases, and conclusion. Check the
+ exact paper verifier, not just an easier relation with a similar name.
+- **Security notion:** unfold enough of `coordinateWiseSpecialSound`, guarded verification,
+ challenge-tree structure, and package composition to confirm that the formal theorem expresses
+ the claimed extraction guarantee.
+- **Abstraction quality:** determine whether one declaration mixes distinct mathematical seams,
+ whether a paper concept has been encoded twice, or whether a large target should be split at a
+ semantic boundary. Keep bridges as statement reshaping; do not conceal protocol work inside one.
+- **Hypothesis minimality:** classify every explicit and typeclass hypothesis as a paper premise,
+ a representation requirement, a downstream composition requirement, or a proof artifact. Trace
+ its use and, in a scratch example, try removing or weakening it. An important paper assumption
+ that Lean never uses is also a warning that the theorem may be too weak.
+- **Vacuity and countermodels:** try boundary and small cases. Check that challenge spaces and
+ relations are inhabited where intended, the verifier is not definitionally always rejecting,
+ witnesses carry the claimed information, an impossible hypothesis is not proving everything,
+ and the extractor conclusion is not true for an irrelevant reason.
+- **Connectivity:** confirm that the audited theorem is the theorem stored in the exported
+ `CWSSPackage`/`GCWSSPackage`, that the package exposes the audited relations and verifier, and that
+ `Composition.lean` consumes that package at the advertised seam. A correct unused theorem does
+ not discharge a milestone.
+
+Apply these Hachi-specific attacks whenever the target can reach the relevant seam:
+
+- Recompute the transitive proof closure through generic CWSS/scalar/guarded composition code and
+ planned prerequisites. A row with one local `sorry` can still depend on missing or sorried
+ generic machinery.
+- For every zero-round adapter, require the exact forward/completeness direction as well as the
+ CWSS pullback direction, or document the precise asymmetric containment that is intended. Prove
+ stack/unstack and encoding round trips; an empty or overly strong output relation can make the
+ pullback theorem meaningless.
+- For every `Bool` guard such as a round, final, or trace check, establish `check = true ↔` the
+ advertised paper equations and an honest-acceptance lemma before proving soundness. An
+ always-false sorried check makes accepting-tree obligations vacuous.
+- When extraction uses `LiftCom.collision_mem`, trace the required shortness proof for both
+ colliding openings back through every relation. A point-evaluation check does not by itself imply
+ coefficient/range shortness.
+- For table and polynomial encodings, require explicit capacity inequalities, index equivalences,
+ padding behavior, reconstruction laws, degree bounds, and the inequalities connecting digit
+ bounds to public shortness parameters.
+- Audit every division or derived omitted value for a nonzero denominator. In particular, a fixed
+ partial-evaluation pivot must not be assumed nonzero for all evaluation points.
+- Do not quantify over an arbitrary basis table, packing map, commitment reinterpretation, or
+ escape conversion and then use unstated algebraic laws. State and justify independence,
+ round-trip, commutation, bound, and escape-validity laws at the right abstraction boundary.
+- Treat corrected Lemma 10 as an explicit protocol repair, not a proof of the paper's printed
+ uniform-vector protocol. Treat the row-11 `Z`-packing pullback as a known open soundness gap until
+ an authorized repair replaces it.
+- Re-audit a “proven” dependency when its documented relation or bound is only a containment or a
+ modeling generalization of the paper and becomes load-bearing for the target.
+
+Produce a concrete non-vacuity certificate: at least one honest symbolic instance/transcript or
+small Lean example, plus a trace showing which verifier checks constrain which output-relation
+facts. Use explicit mathematical counterexamples when useful, but do not use `native_decide`, even
+for experiments. Independently search ArkLib and Mathlib before adding abstractions or hypotheses.
+
+### 3. Correct the skeleton
+
+Resolve every material audit finding now:
+
+- repair definitions and theorem statements rather than compensating in proofs;
+- split the subprotocol only at mathematically meaningful interfaces with explicit relations;
+- remove unused or unjustified hypotheses, and add a missing premise only when it belongs to the
+ paper or an explicitly approved repair;
+- state all useful representation/equivalence/containment and sanity lemmas;
+- wire the exact public theorem into the package and its immediate composition consumer;
+- give every major definition and theorem a docstring with precise paper references and disclose
+ every deliberate divergence.
+
+Keep already-correct proofs. Use `sorry` only for genuine remaining proof bodies. Build each
+affected file after statement changes so the skeleton, package, and seam elaborate together.
+Every data-bearing definition—encoding, relation data, verifier, protocol spec, extractor,
+statement conversion, or witness construction—must be implemented. Never use `sorry` to fabricate
+data and then prove propositions about that fabricated value.
+
+If the paper claim appears false or under-specified, do not silently repair it. Isolate the failing
+claim, produce the smallest rigorous counterexample or missing obligation possible, distinguish a
+faithful model from candidate repairs, and obtain a repair decision unless the target already names
+an approved corrected variant. A dashboard entry marked as an open gap is not discharged by
+assuming the missing implication.
+
+### 4. Repeat until stable
+
+Alternate a builder pass with an adversarial reviewer pass. After each pass, update the contract
+and ledger, fix findings, rebuild, and restart the clean-pass count. Exit Stage 1 only after two
+consecutive passes find no new material error or actionable improvement: one paper/cryptographic
+semantics pass and one Lean abstraction/API/composition pass.
+
+Stage 1 is complete only when:
+
+1. every contract item maps to final Lean declarations with a justified correspondence;
+2. no known vacuity, false seam, silent paper deviation, unnecessary hypothesis, or unjustified
+ abstraction remains;
+3. all intended definitions, theorem statements, helper statements, packages, and seam wiring
+ elaborate together;
+4. every remaining proof obligation is an explicit, inventoried `sorry` in a proof body.
+
+## Stage 2 — develop a feasible proof architecture
+
+This stage may add, generalize, split, relocate, or restate helper lemmas, but every such change must
+remain faithful to the Stage 1 contract. Finish all proof-interface design before freezing.
+
+### 1. Inventory exact goals and dependencies
+
+Use `rg` to locate textual placeholders, then use Lean diagnostics/build warnings to distinguish
+proof terms from comments and to inspect the elaborated goals. Include sorries in local helpers,
+structure fields, package certificates, and imported target-specific prerequisites. Build a DAG and
+plan leaf obligations first.
+
+For each obligation record:
+
+- declaration, file, exact type, local context, and hypotheses actually available;
+- mathematical argument and the invariant or normal form that makes it work;
+- existing ArkLib/Mathlib lemmas to reuse, with checked names and instantiated types;
+- any intermediate lemma, generalization, induction motive, extensionality principle, or coercion
+ normalization required;
+- likely Lean proof steps, expected fragile points, and a fallback route;
+- dependencies and a 1–10 difficulty rating with evidence.
+
+Use the scale honestly: 1 is a direct one- or two-minute proof; 5 is involved but bounded work with
+known mathematics and APIs; 6 or above means the current hole is not ready for the proof phase; 10
+is day-scale or research-level work. No hole may enter Stage 3 at 6 or above.
+
+### 2. Materialize the proof architecture
+
+Search before inventing. Check candidate lemmas with scratch `#check`/`example` declarations and use
+`exact?`, `apply?`, `rw?`, or `simp?` as exploration aids. Add every necessary top-level
+intermediate lemma now, in its natural module, with its final statement and a `sorry` proof. Prefer
+small reusable mathematical facts over protocol-specific duplication, but do not generalize beyond
+a clear use.
+
+Split a hard hole until each resulting obligation is independently below 6. Splitting is legitimate
+only when the helpers express real intermediate facts and do not merely restate the original goal,
+assume its difficult premise, or form a dependency cycle. If an obligation cannot be brought below
+6 without changing protocol semantics, return to Stage 1; if the mathematics itself is missing or
+false, report the blocker rather than manipulating the rating.
+
+### 3. Adversarially validate feasibility
+
+Give a fresh reviewer the current skeleton and proof DAG. Ask it to attack each rating by checking
+theorem names and orientations, universes and typeclasses, finite-index casts, induction motives,
+algebraic side conditions, hidden classical reasoning, circular dependencies, and whether the
+proposed conclusion really follows. Require proof spikes for the riskiest step and any disputed
+rating. A plausible paragraph without an elaborated critical step is not enough.
+
+Revise and repeat until every objection is either fixed or refuted with Lean/math evidence and two
+consecutive feasibility passes introduce no new helper or rating of 6 or above.
+
+### 4. Freeze the semantics
+
+Build the complete sorried skeleton, then create a semantic-freeze manifest and copy the scoped
+files to a fresh `/tmp/prove-milestone-/stage2-baseline/` directory. Record:
+
+- every declaration name and full type, binder order, attributes, instance priority, and namespace;
+- all definitions, relations, verifiers, protocol specs, structures, notation, and imports;
+- package fields and composition wiring;
+- the exact allowed proof-body placeholders;
+- captured `#check @fully.qualified.name` output for every target declaration and `#print` output
+ for each frozen data-bearing definition, relation, verifier, and package constructor;
+- scoped file hashes/diffs, initial user changes, `sorry` locations, and a successful build log.
+
+Do not commit merely to create the freeze. From this point onward, only the bodies of inventoried
+proof declarations may change. If anything else is needed, explicitly return to Stage 1 or 2,
+re-run their exit gates, and create a new freeze.
+
+## Stage 3 — discharge the frozen proof obligations
+
+Work through the proof DAG from leaves to public certificates. For one `sorry` at a time:
+
+1. Re-open its exact goal and follow the reviewed plan.
+2. Replace only that proof body. Proof-local `have` statements are allowed; new top-level helpers,
+ attributes, imports, instances, definitions, or statement changes are not.
+3. Run `lake env lean path/to/File.lean` immediately. Do not accumulate unverified edits.
+4. Recount target-closure proof placeholders and require a strict decrease. Check that no `admit`,
+ `constant`, axiom, or renamed surrogate appeared.
+5. Diff against the Stage 2 baseline. Reject every change outside allowlisted proof bodies, including
+ “harmless” binder, hypothesis, relation, verifier, package, or import edits.
+6. Run the forbidden-construct scan over the changed proof and run `#print axioms` for the proved
+ declaration. It must not contain `sorryAx`, `Classical.choice`, or a project-defined axiom.
+7. Mark the DAG entry proved only after the local build, freeze check, and axiom check pass.
+
+Prefer explicit, maintainable kernel-checked arguments and existing library lemmas. Automation is
+acceptable when it produces an ordinary auditable proof term and passes the axiom audit; it is not
+evidence that the theorem has the intended meaning.
+
+If the plan fails, stop editing that proof. Diagnose whether the issue is a missing helper (return
+to Stage 2), a wrong statement/definition (return to Stage 1), or merely a tactic/API detail (revise
+the proof plan without altering the freeze). Never make a frozen theorem easier in place.
+
+After the last local hole is removed, audit `#print axioms` for every new or changed theorem, the
+milestone's exported certificate, and its package-facing theorem. Also check imported lemmas on
+which the public certificate depends: a locally sorry-free wrapper around `sorryAx` is not a proven
+milestone.
+
+Stage 3 is complete only when the target closure contains no `sorry`/`admit`, every source diff is
+freeze-compliant, every scoped file builds, and all axiom/construct checks pass.
+
+## Stage 4 — independently verify that the right result was proved
+
+Perform a clean-room review from the primary paper and the frozen contract, preferably with a fresh
+reviewer who is not shown the implementation rationale. Re-check:
+
+- the exact claim, quantifier order, hypotheses, dimensions, bounds, transcript structure, and
+ extraction conclusion;
+- message/challenge order, challenge space and distribution, verifier acceptance predicate, and
+ guarded-versus-relational checks;
+- equality/equivalence/containment directions at both seams;
+- non-vacuity and representative boundary cases;
+- that the public theorem is installed in the advertised package and the package is the one used by
+ composition;
+- that no stronger assumption or weaker conclusion entered after the contract was written;
+- that the final theorem and all target-specific dependencies are free of `sorryAx`, classical
+ choice, forbidden constructs, and new axioms.
+
+Compare the final `#check` and frozen-definition `#print` outputs with the Stage 2 captures; require
+exact matches. Source review remains necessary because elaborator output is a backstop, not a
+license to modify semantically relevant syntax.
+
+Try to falsify the result, not merely explain why it seems reasonable. Require two consecutive
+clean independent audits. If either audit finds a material mismatch or improvement, return to
+Stage 1, update the audit ledger, rebuild the skeleton, redo the proof architecture and semantic
+freeze, and re-prove the affected obligations. Do not patch around the finding in Stage 4.
+
+## Final validation and handoff
+
+Once the clean-room review is clean:
+
+1. Run each changed Lean file directly, then `./scripts/validate.sh`; run scoped style linting and
+ the relevant `ReadLints` checks, and use `./scripts/validate.sh --lint` when repo-wide lint debt
+ will not obscure the result. Add `--docs` when Lean docstrings or documentation changed, and run
+ `git diff --check`.
+2. Re-run the target-closure placeholder scan and the complete axiom manifest.
+3. Update `hachi-overview.html` only from verified source facts: file status, exact genuine `sorry`
+ count, milestone description, and “proven” claim. Do not erase a known paper deviation or open
+ gap. Re-run documentation integrity checks after documentation changes.
+4. Update `Composition.lean` provenance, Hachi module docstrings, and `docs/kb/` when their factual
+ account changed. Never hand-edit generated `ArkLib.lean` or derived site output.
+5. Run the self-improvement pass below before writing the final report.
+
+## Self-improvement pass
+
+Run this pass exactly once at the end of every invocation, after all milestone work and verification
+that can be completed. Run it even when the milestone ends at a legitimate false-claim, missing-
+authority, or external-blocker exception. This pass is part of the task: do not merely suggest
+improvements for a future agent. Apply every accepted, in-scope improvement to this canonical skill
+file before the final response.
+
+### 1. Derive candidates from evidence
+
+Review the scope manifest, audit ledger, stage restarts, invalidated proof ratings, reviewer
+findings, verification failures, and user corrections. Identify places where this skill was
+ambiguous, incomplete, stale, inefficient, too permissive, or missing a reusable Hachi-specific
+check. Do not infer a workflow defect merely because the targeted mathematics was hard.
+
+For each candidate record:
+
+| Field | Required content |
+| --- | --- |
+| Evidence | The concrete event, failure, or repeated friction observed during this run |
+| Generality | Why another Hachi milestone is likely to encounter it |
+| Proposed edit | The exact instruction to add, remove, tighten, or reorganize |
+| Risk | Possible overfitting, contradiction, excess cost, or loss of useful freedom |
+| Decision | Apply, reject as one-off, or defer because it needs user authority |
+
+Accept a candidate only when it is supported by the run, likely to recur, materially improves
+correctness or efficiency, and is consistent with `AGENTS.md`, the user's constraints, and the
+maintenance rule in `docs/skills/README.md`. Prefer tightening or simplifying an existing
+instruction over appending a duplicate. Do not add task-specific theorem names, transient line
+numbers, a success story, or a changelog unless they encode a stable class of failure.
+
+### 2. Apply accepted improvements
+
+Patch `docs/skills/prove-milestone.md` in the same run. Preserve its name, four-stage architecture,
+semantic-freeze guarantee, constructive proof policy, and completion gates unless the user
+explicitly changes them. Keep the file below 500 lines and remove superseded wording so the skill
+does not grow monotonically. Do not modify milestone source files during this meta-pass.
+
+If no candidate meets the acceptance rule, make no cosmetic edit. Explicitly report that no
+durable improvement was found and give the evidence considered. “No change” is preferable to an
+unsupported self-modification.
+
+### 3. Validate the revised skill
+
+Re-read the complete skill rather than only the edited hunk. Check for contradictory stage gates,
+broken links, duplicated rules, weakened safety requirements, line-count growth, and instructions
+that would cause an infinite loop. Run documentation integrity and `git diff --check`. When the
+edit materially changes behavior and subagents are available, give a fresh agent the revised skill
+and a realistic Hachi target as a forward test; revise again if that test exposes a defect.
+
+Do not recursively run a new self-improvement pass because this pass edited the skill. Fix direct
+validation defects within this same pass, then stop after the revised skill validates.
+
+### 4. Report the self-improvement
+
+Include a **Skill self-improvement** section in the final response. List the evidence-backed
+candidates, which edits were applied and where, which candidates were rejected or deferred and why,
+and the validation run on the revised skill. Do not call the overall invocation complete before
+this report and the accepted edits both exist.
+
+Report:
+
+- the target and exact paper contract conclusion;
+- definitions/statements changed before the freeze and why;
+- the final proof DAG with ratings and statuses;
+- hypotheses removed, retained, or added, with justification;
+- semantic-freeze compliance;
+- `#print axioms` results for the public declarations;
+- validation commands and results;
+- dashboard/documentation updates;
+- skill-improvement candidates, applied edits, rejected/deferred candidates, and skill validation;
+- any remaining out-of-scope sorries or upstream trust assumptions, clearly separated from the
+ discharged milestone.
+
+Do not call the milestone complete unless all four stage gates pass. The only legitimate terminal
+exception is a demonstrated false/under-specified paper claim or a required repair decision outside
+the authorized scope; report that as a formalization finding, not as a successful proof.
diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md
index ef264c4470..1d12b949a5 100644
--- a/docs/wiki/repo-map.md
+++ b/docs/wiki/repo-map.md
@@ -103,10 +103,42 @@ home_page/ site assets and assembled website root
(`toQuadEvalStatement`), the pulled-back input relation `relPolyEval`, and its CWSS
`bridge_coordinateWiseSpecialSound`. `QuadEval.lean` re-exports the reduction, its soundness,
and the bridge.
+ - §4.3 (Hachi's sumcheck-based opening, Figures 4–7) is a **skeleton** split into one flat
+ folder per paper subprotocol figure (peers of `QuadEval/`), each file exporting a
+ `CWSSPackage`/`GCWSSPackage` with a sorried CWSS theorem, plus the front-threading file
+ `Escape.lean` at the Hachi root:
+ - `Escape.lean` — the escape-threaded front `evalChainE` (design G1): widens the finished
+ `QuadEval` front relations with an abstract weak-binding escape budget so every §4.3 seam has a
+ home for the `w̃`-commitment's binding break. Front glue, not a §4.3 subprotocol; sits at the
+ Hachi root beside `EvalSplit`/`Composition`.
+ - `RingSwitch/` (§4.3 entry, Figure 4 / Lemma 9) — the HMZ25 **ring-switching lift** reducing
+ `R^lin` to a claim about the committed lifted witness evaluated at a random `α`. `RingSwitch/Rlin`
+ is the zero-round Eq. (20) → `R^lin` adapter (F2); `RingSwitch/Reduction` is the two-round lift
+ (`k = 2d`, the abstract `w̃`-commitment `LiftCom`). `RingSwitch.lean` re-exports the folder.
+ (Distinct from the §3 packing reduction under `ProofSystem/RingSwitching/`, also a ring-switch.)
+ - `ZeroCheck/` (§4.3, Figure 5 / **corrected** Lemma 10) — reduces the batched identities
+ `H₀ ≡ 0 ∧ H_α ≡ 0` to random-point evaluations. `ZeroCheck/Constraints` is the **shared**
+ encoding (Eqs. (21)–(23): the table `w̃`, `H₀`/`H_α`, the sumcheck polynomials, degree pins,
+ the Kronecker curve `kroneckerPoint`, per-round seam `roundRel`), consumed by both this
+ zero-check and `Sumcheck/`; `ZeroCheck/Batch` is the per-row/range ⇄ `H₀/H_α ≡ 0` batching
+ bridge; `ZeroCheck/Reduction` is the corrected Lemma 10 (Kronecker seed pair, `(ℓ, k) = (2, D)`;
+ see `HACHI_LEMMA10_GAP.md`). `ZeroCheck.lean` re-exports the folder.
+ - `Sumcheck/` (§4.3, Figure 6 / Lemma 11 + Figure 7 tail) — the sumcheck loop finishing the
+ opening. `Sumcheck/Bridge` reshapes the zero-check's point claims into the initial hypercube
+ sums; `Sumcheck/Rounds` is the `m₀`-round guarded paired sumcheck (loop by recursion over
+ `▷ᵍ`); `Sumcheck/FinalEval` is the guarded reveal of `w̃(a)` (Figure 7 tail) landing on the
+ recursion's evaluation claim. `Sumcheck.lean` re-exports the folder.
+ - `Recursion/` (§4.5) — the recursion adapters: `PartialEval` (Eq. (24) peeling, pure
+ derive-`y₀`), `ZBatchBridge` (Eqs. (25)–(26) `Z`-packing — ⚠ carries the open
+ partial-evaluation soundness gap, `HACHI_RECURSION_GAP.md`), `TraceHandoff` (Eqs. (27)–(28)
+ — guarded trace check, lands on the next iteration's `QuadEval` seam over `Φ'`).
- `Composition.lean` — the **CWSS composition home**: `evalChain` is the `bridgePackage ▷
quadEvalPackage` chain and `eval_coordinateWiseSpecialSound` is its composed CWSS certificate
- (`sorryAx`-free). Each further §3/§4.3+/§4.5 subprotocol lands as one more `CWSSPackage`
- `▷`-appended here.
+ (`sorryAx`-free). `openCore` chains the escape-threaded front with the pure §4.3 links (rows
+ 1–7 of the header's seam table), and `openingChain` /
+ `hachi_iteration_coordinateWiseSpecialSound` compose the guarded tail (sumcheck loop, final
+ eval, recursion adapters) into the full one-iteration certificate — a skeleton whose sorry
+ provenance is inventoried in the module header.
- `Commitment.lean` — **Hachi as a `Commitment.Scheme`**: the eval `OracleInterface`, honest
`keygen`/`commit` (canonical base-`b` gadget decomposition at width `δ = ⌈log_b q⌉`), and the
`hachi` scheme value (its opening `Proof` is a documented `sorry` pending the remaining §4.3+
@@ -149,8 +181,14 @@ home_page/ site assets and assembled website root
(e.g. Hachi's `bridgeVerifier`). `SingleRound` is the generic single-challenge-round navigation
layer (tree shape recovery `tree_shape`, the star-center machinery, the tree extractor
`E`, and the assembly `coordinateWiseSpecialSound_of_mkWitness`) used by Hachi's polynomial-
- evaluation reduction `QuadEval` (Lemma 8). The umbrella `CoordinateWiseSpecialSoundness.lean`
- re-exports the core files.
+ evaluation reduction `QuadEval` (Lemma 8). `ScalarRound` is its skeletonized `(ℓ = 1, k)`
+ scalar-challenge twin (`pSpecScalar`, `scalarStructure`; assembly sorried) for Hachi's
+ Lemmas 9/11-shaped rounds. `Escape` provides `Set.withEscape`, the escape-threading of
+ relations (`W ⊕ E` witnesses) used by composed extraction chains that can emit binding-break
+ escapes mid-chain. `Guarded` is the **B4 skeleton**: `Verifier.IsGuardedWith`/`IsGuarded`
+ (runtime-rejecting verifiers), the guarded package `GCWSSPackage` with its append `▷ᵍ`, and
+ the (sorried) guarded binary CWSS append theorem. The umbrella
+ `CoordinateWiseSpecialSoundness.lean` re-exports the core files.
- Active areas are often grouped by paper or protocol family, for example
`Data/CodingTheory/ProximityGap/BCIKS20/...` or `ProofSystem/Binius/...`.
- Ring switching is a **generic, instantiable compiler** under `ProofSystem/RingSwitching/`, not a