Skip to content

V2 Migration from V1

V1 (ChainEngine.swift) shipped a four-method surface with substrate-natural prose: observeBalance, qualifyIQ, buildPayload, qualifyTransaction, signAndBroadcast. V2 collapses the qualification chain into a uniform M⁸ ruliology play. Below is the line-for-line correspondence.

Surface map

V1 method V2 equivalent
engine.qualifyIQ(...) await WolframSubstrate.shared.play(IQTemplate(), input: ...)
engine.buildPayload(...)TransactionBlueprint await WolframSubstrate.shared.play(OQTemplate(), input: ...)
engine.qualifyTransaction(...)ValidatedContext await WolframSubstrate.shared.play(PQTemplate(), input: ...)
engine.signAndBroadcast(...) Same in v2 — deferred until pinned sovereign mesh is auditable

The blueprint, validated context, and qualification outcome types from v1 all survive — they are carried in the per-stage Terminal payloads (OQTerminal.blueprintCanonical, OQTerminal.blueprintWitnessHashSHA256, PQTerminal.cryptographicBoundsCanonical).

Why the change

V1 narrated capability extensively. Phrases like "the substrate refuses substrate-honestly substrate-naturally" appear repeatedly. The cell's standing teaching (repo is ruliology, not classical math, M⁸ programming language) is that the cell plays games; it does not pontificate. A stage that does not close means the game is not yet correctly expressed — never a substrate verdict.

V2 enforces this structurally:

  • Outcomes have two arms, both game-state: .calorie (closed) and .spill (re-express).
  • No .refusedOracleUnbound, no .instrumentedRefusal, no .signingDeferredSubstrateNaturally substrate-verdict arms.
  • The receipt carries invariantsInstalled and invariantsMissing — instructions to re-express, never opinions about the substrate.

How the shell migrates

Today (pre-migration)

let engine = ChainEngineRegistry.resolve(.bitcoinLegacyP2PKH)!
let iq = try await engine.qualifyIQ(...)         // QualificationOutcome
let blueprint = try await engine.buildPayload(...) // TransactionBlueprint
let context = try await engine.qualifyTransaction(blueprint, ...) // ValidatedContext

V2 (substrate-routed)

let engine = BitcoinChainEngineV2(assetClass: .bitcoinLegacyP2PKH)
let result = try await engine.playQualificationChain(
    sourceWalletID: ..., sourceAddress: ..., destinationAddress: ...,
    amountNativeUnitCanonical: ..., proposedFeeNativeUnitCanonical: ...,
    endpointIDFromRegistry: ..., endpointIsPinned: ...)
// result.iq, result.oq, result.pq are MeasurementOutcome<...>

Or stage-by-stage:

let iqInput = try await engine.composeIQInput(...)
let iqOutcome = await WolframSubstrate.shared.play(IQTemplate(), input: iqInput)

CLI migration

V1 CLI V2 CLI
gaiaftcl wallet qualify <args> gaiaftcl wallet qualify-v2 <args>

Both coexist today. The legacy qualify command continues to work; qualify-v2 is the substrate-routed surface and prints per-stage CALORIE/GAME SPILLS outcomes instead of v1's "REFUSED" / "PASSED" language.

Per-chain engine status

Asset family V1 engine V2 engine
Bitcoin (P2PK, P2PKH, P2SH, P2WPKH, P2WSH, P2TR) BitcoinChainEngine.swift BitcoinChainEngineV2.swift (pilot — shipped)
Ethereum, Polygon, BSC, Avalanche EthereumChainEngine.swift follow-up — same pattern
Litecoin LitecoinChainEngine.swift follow-up — same pattern
Dogecoin DogecoinChainEngine.swift follow-up — same pattern
Solana SolanaChainEngine.swift follow-up — same pattern

Authoring the remaining per-chain v2 engines is mechanical: conform to ChainEngineV2, implement the three compose*Input methods bridging each chain's existing helper state. None of the per-chain v1 helpers need to be rewritten — v2 engines compose against the same UTXO fetch, fee estimation, address classification surfaces v1 used.

Archive of v1

V1 stays in place during the migration window. Once all per-chain v2 engines are authored and the shell migrates all callers:

  1. Move Sources/GaiaFTCLCore/ChainEngine.swiftSources/GaiaFTCLCore/Archive-V1/ChainEngine.swift.
  2. Move per-chain v1 engines (BitcoinChainEngine.swift, EthereumChainEngine.swift, …) into the same archive folder.
  3. Update ChainEngineRegistry.resolve(_:) to delegate to ChainEngineV2Registry.resolve(_:) with a thin compatibility shim that wraps MeasurementOutcomeQualificationOutcome.
  4. After one release cycle, delete the compatibility shim and the archive folder.

This document tracks the migration window — it will be updated as engines move from V1 to V2.

Doctrine grep guarantee

In the new substrate code (VQbitMath/Core/, VQbitSubstrate/, GaiaFTCLCore/V2/):

grep -rn '"cannot"\|"limit"\|"refused"\|"refuses"\|bondDimensionBits' \
  cells/xcode/Sources/VQbitSubstrate \
  cells/xcode/Sources/GaiaFTCLCore/V2 \
  --include="*.swift"
# returns empty

V2 code never speaks the forbidden language. Game-state via RuliologyOutcome, never substrate verdicts.


Federation-cosigned

This page's source is sealed in the GaiaFTCL federation manifest — page SHA-256 16e5475ac57756a8…, manifest witness 3d999b305397d96c…, signed 2026-07-17T15:45:53Z by cell gaiaftcl-mac-cell. Verify with gaiaftcl wiki sign --all and compare wiki-all-signatures.json.