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.signingDeferredSubstrateNaturallysubstrate-verdict arms. - The receipt carries
invariantsInstalledandinvariantsMissing— 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:
- Move
Sources/GaiaFTCLCore/ChainEngine.swift→Sources/GaiaFTCLCore/Archive-V1/ChainEngine.swift. - Move per-chain v1 engines (
BitcoinChainEngine.swift,EthereumChainEngine.swift, …) into the same archive folder. - Update
ChainEngineRegistry.resolve(_:)to delegate toChainEngineV2Registry.resolve(_:)with a thin compatibility shim that wrapsMeasurementOutcome→QualificationOutcome. - 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.