Plan T2 — Registry Migration (sub-plan of Plan T umbrella)
On this page
Status
| Step | Description | Status |
|---|---|---|
1 |
[#570] T2.1 AnyAdapter → registry dispatch + bundle orchestrator + ExchangeRegistries. Hard mechanical gate at MR open: F-065 / Plan G Step 6 / #462 must be * NEW |
Done (2026-06-10) — MR !673 / |
2 |
[571] T2.2 AuditCodecRegistry wiring + audit seam deletion. Wire |
Done (2026-06-10) — scope narrowed (T2.1 deviation: |
3 |
[572] T2.3 Mock-server → MockRouterRegistry + per-partner mock modules + MockState deletion. Per ADR-032 A5 + §2.5 + D8. Scope: * NEW leaf crate |
Done (2026-06-10) — shipped per spec with ONE structural deviation: |
4 |
[#573] T2.4 Drop adapter_kind CHECK constraint + boot membership validation. Per ADR-032 §1.3 (dual-site validation closes TOCTOU) + §2.4 as amended by A7 (boot validates REGISTRY MEMBERSHIP only; federal-mapping completeness is report-emit-time, T3.4 scope). Scope: * NEW migration |
Done (2026-06-10) — shipped as specified, executed BEFORE T2.3 (steps mutually independent per the DAG). Migration |
5 |
[#574] T2.5 plan-completion audit + archive (Plan T2). Standard close-out per |
Done (2026-06-10) — this MR (audit + archive close-out). Audit subagent dispatched pre-archive: all 4 step cells verified against code reality (spot-checks PASS), 0 stale doc references outside the expected archive rewrites; T2.3 cite backfilled (!677 / |
Context
Plan T1 (body) lands the foundations: ErasedAdapter trait + impl_erased_adapter! macro (T1.2), AuditCodec + 10 codec impls (T1.3), craig-state-bundle with BundleContribution/StateBundle/BootContext/5 registries (T1.5), craig-state-default::DefaultBundle (T1.6), per-partner + host-service macro adoption (T1.7). Plan T2 MATERIALIZES those foundations into the running system:
-
T2.1 replaces the closed-enum
AnyAdapterdispatch (the Plan L Step 2a wrapper atservices/craig-exchange/src/adapters/mod.rs:41-78) with registry-resolvedArc<dyn ErasedAdapter>dispatch at the 2 production call sites. The bundle orchestrator bootsDefaultBundlehardcoded (multi-bundle env-var activation is Plan U Step 4 per A10). -
T2.2 does the same for the audit seam (
audit_typed→AuditCodecRegistry). -
T2.3 makes the mock-server bundle-driven per A5/§2.5 — mock modules move to per-partner crates behind
mockfeatures; the sharedMockState(verified unused) dies per D8. -
T2.4 opens the DB closure: the
adapter_kindCHECK constraint drops; boot registry-membership validation replaces it (dual-site per §1.3).
After Plan T2 archives: Plan T3 (open ExchangeAdapterKind + PartnerType enums, georgia seed removal, test hardening) executes; Plans U/V gain their trait+registry stability gate.
Decisions inherited
-
D4: F-065 / Plan G Step 6 / #462 mechanically hard-gates T2.1 (
glab issue view 462). -
D5:
ExchangeAdapterKindenum survives Plan T2 UNCHANGED — deletion is T3.1. T2 dispatch converts enum → token via strum at the resolve seam. -
D8: Mock partners self-state-bound; shared
MockStatedeleted in T2.3. (Verified: zero state readers — modules ship stateless; self-state-binding documented as the future pattern.) -
A3:
noop://sentinel INLINE at dispatch entry (ExchangeRegistries::resolve), NOT a registry entry. -
A5: mock modules at
crates/craig-partner-*/src/mock.rsbehindmockCargo feature;MockRouteFactory = Arc<dyn Fn() → axum::Router + Send + Sync>; feature propagation throughcraig-state-default. -
A7: boot validates adapter_kind + partner_type REGISTRY MEMBERSHIP only; federal-mapping completeness is report-emit-time (T3.4).
-
A10: Plan T ships hardcoded
DefaultBundleactivation;CRAIG__ACTIVE_STATE_BUNDLESis Plan U Step 4. -
Plan T1 §SHINES placement architecture: SHINES factory injection is ORCHESTRATOR-SIDE after bundle merge;
craig-state-defaultcontributes 10 factories; double-registration fails boot.
Sequencing gates
-
T2 execution start (T2.1 MR opens): Plan T1 archived AND #462 closed (mechanical check in T2.1 cell).
-
T2.1 → T2.2 (audit seam swap needs
ExchangeRegistries+ orchestrator). -
T2.1 → T2.4 (boot validation extends the T2.1 orchestrator; constraint drop is safe only once registry dispatch is live).
-
T2.3 is independent of T2.2/T2.4 once T2.1 lands (mock-server consumes
MockRouterRegistryviacraig-state-default/mock; no exchange-service coupling) — may run in parallel. -
T2.5 last.
Calendar
| Step | Scope | Anticipated MRs | Calendar |
|---|---|---|---|
T2.1 |
Orchestrator + ExchangeRegistries + dispatch migration + AnyAdapter deletion |
1 |
~1.5 days |
T2.2 |
AuditCodecRegistry seam + audit machinery deletion |
1 |
~0.5 day |
T2.3 |
Mock migration (validation crate + 10 modules + features + mock-server rebuild) |
1 |
~1.5 days |
T2.4 |
CHECK constraint drop + boot membership validation |
1 |
~0.5 day |
T2.5 |
plan-completion audit + archive |
1 |
~0.5 day |
Total: 5 MRs / ~1 wk (sequential sum 4.5 working days ≈ 1 wk; T2.2/T2.3/T2.4 parallelism can compress the critical path to ~3.5 days — the ~1 wk figure is the sequential upper bound matching Plan T umbrella Calendar row 2 "~5 \| ~1 wk").
Step DAG
[gate: Plan T1 archived + #462 closed]
↓
T2.1 (orchestrator + registry dispatch)
↓ ↓ ↓
T2.2 T2.3 T2.4
(audit) (mock) (DDL + boot validation)
└─────────┬┴─────────────┘
↓
T2.5 (audit + archive)
T2.2 / T2.3 / T2.4 are mutually independent after T2.1; any order or parallel.
Threat model
-
Registry dispatch silently changes SHINES behavior — the legacy path threaded
exchange_formatat CONSTRUCTION (StandardAdapter::new(config, format, client)at mod.rs:405-409); the erased path threads it PER-CALL (send_value(…, Some(&partner.exchange_format))). A threading bug sends SHINES payloads with the wrong format. Mitigation: T2.1 keeps the existing send_worker devstack integration tests green + adds a format-override unit test on the passthrough macro arm (extends T1.7’s 3-test battery). -
Connectivity-test trait gap discovered late —
ErasedAdapter(T1.2 spec) ships withouttest_connectivity; T2.1 needs it atpartners.rs:302. Mitigation: the T2.1 cell carries an explicit VERIFY-OR-ADD clause — verify whether T1 execution already added the method; if absent, addtest_connectivity_valueto the trait + BOTH macro arms (additive; the 11 T1.7 invocation-site impls regenerate automatically via the macro). -
Mock migration breaks E2E / devstack silently — the §2.5
/partner/<kind>adoption changes ALL 10 mock route paths (a uniform, audited migration — not a silent drift). Mitigation: datagen (10 URLs) + all 10 round_trip suites updated in the SAME T2.3 MR; 5-surface audit in the cell; full e2e battery on T2.3’s pre-push. -
Dev-dep cycle confuses future contributors —
partner crates →(dev) mock-server →(prod, via state-default) partner crateslooks like a cycle. Mitigation: T2.3 documents the legality (Cargo dev-dep cycle exemption; serde/serde_derive precedent) intools/craig-mock-server/Cargo.tomlcomments + the body’s research note. -
Boot validation bricks devstack on stale rows — post-T2.4 a seeded row with an unregistered adapter_kind fails service boot (intended fail-fast, but devstack reseed must precede). Mitigation: T2.4 ships with reseed in its verification steps; seeded kinds are the 11 registry-backed tokens already.
-
T1 execution deviates from the T1 body specs this T2 body cites — e.g. registry field shapes or macro arms change during T1 review. Mitigation: risk row 9; T2.1 MR opens only after T1 archived, so drift is visible at T2 execution start; body refresh is a small docs MR.
Cross-cutting invariants
| Invariant | Enforced by | Verification |
|---|---|---|
noop:// sentinel handled INLINE, never a registry entry (A3) |
|
T2.1 + T2.4 tests |
SHINES injected orchestrator-side AFTER bundle merge; double-registration fails boot |
|
T2.1 test battery |
Audit encoding keys on the RESOLVED adapter’s |
|
T2.2 test battery |
Boot validates REGISTRY MEMBERSHIP only — no federal-mapping completeness at boot (A7) |
T2.4 validation queries adapter_kind (+ partner_type at T3.5); no |
Code review + A7 cited in T2.4 commit |
Per-partner crates compile WITHOUT axum at default features |
|
|
|
T2.1’s |
|
Typed |
T2 touches only the erased seam + dispatch host |
|
Quality budgets monotonic (B3a watch: |
|
Pre-push gate |
Axis coverage: new tests tag |
|
Pre-push gate |
|
|
Pre-push gate |
NEW crate |
Plan H Step 10 + Plan M Tier-3 precedent |
Crate lib.rs review |
Mock routes nest at |
Registry-driven nesting; |
T2.3 router smoke test |
Risk register
| # | Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|---|
1 |
SHINES per-call format threading regression |
SHINES payload corruption |
Med |
Devstack send_worker integration tests + format-override unit test on passthrough arm |
2 |
|
T2.1 scope growth |
Low |
Macro-generated impls update automatically; only the macro definition + trait change by hand |
3 |
§2.5 |
Hidden test failure |
Low |
5-surface audit in T2.3 cell covers datagen + all 10 round_trip suites + /health; grep |
4 |
Dev-dep cycle confuses contributors or a future cargo version tightens rules |
Build friction |
Low |
Documented in Cargo.toml comments; serde precedent; revisit if cargo changes |
5 |
Boot validation bricks a devstack with stale seeded rows |
Dev friction |
Low |
T2.4 verification includes reseed; pre-1.0 destructive posture |
6 |
|
Reviewer confusion |
Low |
Body documents the realization choice + Plan I F-026 Extension-pattern precedent; same intent (no |
7 |
T2.3 mock-server rebuild breaks |
Test breakage ×10 |
Med |
|
8 |
MockState deletion loses state a module silently needed |
Mock regression |
Low |
Verified 2026-06-09: zero readers/writers on all 9 DashMaps; round_trip battery is the regression gate |
9 |
Plan T1 execution deviates from the T1-body specs this body cites (registry shapes, macro arms) |
T2 body refresh needed |
Med |
T2 execution gated on T1 archive — drift visible before T2.1 opens; refresh is a small docs MR; D-decisions + ADR anchors make large drift unlikely |
Related decisions
-
ADR-032 — §1.2 (AdapterRegistry) + §1.3 (dual-site validation / TOCTOU) + §2.4 (boot membership per A7) + §2.5 (mock-manifest contract) + A3 (noop sentinel) + A5 (mock_routes sourcing + feature gating) + A10 (hardcoded DefaultBundle activation).
-
ADR-038 — §3 factory shape + boot-time materialization rationale for T2.1.
-
ADR-030 — Status vocabulary.
-
Plan T umbrella — parent; this body fills Status row 3.
-
Plan T1 — foundations consumed: T1.2 trait/macro, T1.3 codecs, T1.5 bundle crate, T1.6 DefaultBundle, T1.7 macro adoption + §SHINES placement architecture.
-
Plan S — grandparent; Plan T2 sits at Plan S Step 6 (body filed) / Step 7 (executed).
-
Plan G — F-065 / #462 HARD-gated T2.1 per D4 (closed 2026-06-10 via !671; gate CLEAR).
-
Plan L —
AnyAdapter(Step 2a) +audit_typed(Step 5) + mock-server F-063 round_trip suites (Step 7) are the surfaces T2 migrates; typedExchangeAdapterinvariant preserved. -
the pre-1.0 destructive-rebuild posture — T2.4 destructive constraint drop + T2.3 datagen URL change cite.
-
the service-initialization pattern —
bundle_orchestratorboot wiring slots into the canonicalbootstrap → deps → workers → routermain shape. -
the quality-budget enforcement gate — B3a discipline: Value sites MOVE with dispatch code (markers travel), no net-new Value surface.
-
#462 (F-065) — HARD gate on T2.1.
-
#558 (closed-aggregator) — T2.2’s fail-closed registry-miss path inherits the limitation; unchanged.