The two mains shared no history, so this is a per-file three-way merge:
for each file the 65 forge-only commits touched (by patch-id), the base is
the newest version both histories held. 105 files merged or took one side
cleanly, 48 were already identical, and the 45 true conflicts and 31
modify/delete pairs were resolved by what runs on the cluster:
- The forge wins where it is live: explorer 1.5.56 (digest 45db11ff) and
its archive readers, explore-fe-hanzo's ConfigMap env (AI coin,
/v1/indexer/hanzo, /v1/chain/hanzo/rpc), exchange-api reading
luxd-archive, and explore-fe-{lux,zoo}-testnet, pinned to what they run
(v2.8.5-lux / v2.8.4-lux).
- GitHub wins where it is live: explore-fe-lux v2.8.5-lux, explore-fe-zoo
v2.8.4-lux, bankd v1.0.18, lux-build's routes.
- The forge's retirement of workloads that run nowhere stands: lux-mainnet
dex, lux-hb2, lux-market, traffic, wallet-web; the lux-testnet frontends,
indexer, exchange and gateways; lux-operator. deploy/lux-testnet/luxd.yaml
stays, since roll_luxd.sh reads it.
- GitHub's deletions stand: lux-finance and lux-financial (static sites
now), the k8s/ operator manifests, .hanzo/workflows/cicd.yml.
- Paths are /v1/chain/C/rpc where the forge had written /v1/chain/c. All
four spellings answer; this one is the documented form. The forge's
/{network}/v1 doc lines are dropped: a network is a host, and the prefix
404s.
Dry-run against the running objects (server-side, as CD applies), the
merged explorer shows no change where GitHub's showed 152 lines, and no
other live workload moves further from what runs. Every deploy/*/*.yaml
renders with charts/app; price.yaml, which failed on GitHub, now renders.
Claude-Session: https://claude.ai/code/session_01TzirYkGmD88w4GipqivUtV
CRD group finalization: lux.network/Lux* → lux.cloud/*
Runbook + recorded applies for finishing the half-done CRD group migration on
lux-k8s. This is the gate that must close before Wave 1C
(luxfi/operator shim conversion). It is an operational runbook, not a session
summary.
Why
A prior session began moving the live operator CRs off the legacy group
lux.network (kinds LuxNetwork/LuxGateway/LuxChain/LuxMPC) onto the
canonical group lux.cloud. It applied new-group copies but:
- left the
LuxNetworkcopies lossy (droppedtenantImports, renamedpluginSource.vmPlugins[].vmID→vmIdwhich the operator Go struct does not read — tag isvmID), - never annotated provenance on either side,
- never confirmed parity.
If Wave 1C strips luxfi/operator to a shim owning only lux.cloud/*, any
legacy CR without a faithful lux.cloud equivalent orphans. This runbook makes
every legacy CR faithfully present on lux.cloud and records the provenance
annotations so the legacy CRs/CRDs can be deleted later under one more check.
Reconciliation reality at migration time
lux-system/lux-operator is 0/0 replicas (dormant). No
controller-manager pod runs for either group. Consequence:
- The legacy
phase: Running/Readyvalues are frozen literals — every legacyLuxNetworkhasobservedGeneration: nulland emptyconditionswhilemetadata.generationis 22–29. Nothing reconciled them; nothing keeps them Ready. - The live
luxdStatefulSets (5/5, 113d) and theapi-gatewayworkloads run independently of any reconcile loop. Applying corrected desired-state CRs therefore touches no live workload. - A live
status.phase == Readyon the new-group CRs is not obtainable until Wave 1C activates the shim operator. The verification gate used here is spec parity (every legacy field present on the new-group CR with matching value), and live-Ready confirmation is explicitly deferred to Wave 1C via thelux.network/migration-statusannotation. No Ready value was fabricated.
Canonical-kind decision
luxfi/operator go/api/v1/luxnetwork_types.go:642 — LuxNetwork is the
canonical Kind and its controller (network_lux.go, For(&LuxNetwork{}))
owns the luxd StatefulSet + EVM plugin lifecycle. Network
(plural networks, network.go) is a separate unified kind, empty in
cluster, not the luxd driver. So the migration target for the live luxd CRs
is luxnetworks.lux.cloud (kind LuxNetwork), not networks.lux.cloud.
Both CRDs were created together in the 2026-06-02 "29-kind union" regeneration.
Inventory (BEFORE → AFTER)
| Family (legacy → new) | Count | Legacy phase (frozen) | Migration |
|---|---|---|---|
luxnetworks.lux.network → luxnetworks.lux.cloud |
3 | Running | re-applied corrected (restored tenantImports, fixed vmID, kept forward exportSchedules/quasarTimestamp/v1.30.3/adoptExisting) |
luxgateways.lux.network → gateways.lux.cloud |
3 | Ready | already byte-identical; annotated |
luxchains.lux.network → chains.lux.cloud |
11 | Pending (never reconciled) | already byte-identical; annotated |
luxmpcs.lux.network → mpcs.lux.cloud |
1 | (no status) | already migrated (image string→struct, lossless); annotated |
luxindexers/luxexplorers legacy CRDs exist but hold zero CRs — nothing
to migrate. luxsubnets has no CRD on this cluster.
The 11 LuxChain "Pending"
They are declarative shadows of plugins the luxd StatefulSet already
serves (aivm, bridgevm, dexvm, graphvm, identityvm, keyvm, thresholdvm,
oraclevm, quantumvm, relayvm, zkvm — one per pluginSource.vmPlugins entry).
Created 2026-04-30 after the operator was already dormant, so they were never
reconciled (observedGeneration: null, no owner refs, empty conditions). They
drive no live workload. They are NOT controller-orphans (the controller was
never up for them). Resolution: faithfully present on chains.lux.cloud (specs
byte-identical) and annotated. Not stale, not deleted — kept until Wave 1C can
reconcile or formally retire them.
Actions taken (recorded)
- Captured BEFORE state →
before/. - Built corrected
LuxNetwork.lux.cloudmanifests = full legacy spec (production source of truth) + additive forward fields from the prior edit. →manifests/network-*.yaml. kubectl apply --server-side --force-conflicts --field-manager=crd-group-migration -f manifests/network-*.yaml(adopted the fields owned by the priorkubectl-client-side-apply/kubectl-patchmanagers). Dormant operator ⇒ no live workload change.- Verified parity: 18/18 CRs — every legacy field present on the new-group CR with matching value (mpc image string↔struct ignored as documented forward migration). Gate passed before annotating legacy.
- Annotated new-group CRs:
lux.network/migrated-from,lux.network/migration-date. - Annotated legacy CRs:
lux.network/superseded-by,lux.network/migration-status(records deferred Ready-check),lux.network/migration-date. Specs untouched — legacymetadata.generationunchanged (27/22/29).
Reproduce / re-apply
ctx=lux-k8s
kubectl --context $ctx apply --server-side --force-conflicts \
--field-manager=crd-group-migration -f manifests/network-lux-mainnet.yaml \
-f manifests/network-lux-testnet.yaml -f manifests/network-lux-devnet.yaml
The gateway/chain/mpc manifests in manifests/ are the live new-group objects
as applied by the prior session (cleaned of server fields); they are recorded
for the audit trail and are idempotent to re-apply.
NEXT — Wave 1C cutover
When the shim operator (lux.cloud → bootno.de) is live:
- Confirm each
luxnetworks.lux.cloud/luxdreachesstatus.phase: Readyand stays Ready across one reconcile + one rollout. Same for gateways/mpc. Decide whether the 11chains.lux.cloudshould reconcile or be retired. - Only then
kubectl deletethe legacylux.networkCRs (they carrysuperseded-by) and finally the legacy CRDs (luxnetworks/luxgateways/luxchains/luxmpcs.lux.network). - Do not delete before the Ready-persistence check — that is the CTO mitigation this runbook is built around.