ADR-0096 — Lab Deployment Staging wizard Phase B-late (5-PR wave)¶
- Status: Accepted
- Date: 2026-05-25
- Deciders: André Luiz Gallon
- Surface: Dashboard
/admin/lab-staging(multi-phase wizard) + new K8s CronJob slot - Wave: WIZ-B1 + WIZ-B2 + WIZ-B3 + WIZ-B4 + WIZ-B5 (5 sub-PRs)
- Strategy memo: discuss_lab_deployment_staging_2026_05_09.md
- Related ADRs: ADR-0095 (Lab Staging wizard wave) · ADR-0011 (Topology axes) · ADR-0017 (Backup/DR) · ADR-0088 (ml-cortex sidecar)
- Patent claim: none (UX/UI + ops feature)
Context¶
ADR-0095 shipped the 6-PR wizard wave with 8 explicit Phase B-late items in its carry-forward list. ADR-0096 closes those 8 items via 5 thematic sub-PRs (WIZ-B1..B5) while honoring two scope decisions:
- react-flow swap stays deferred — Q4 LOCKED in ADR-0095 says HTML5 native drag-drop is sufficient for MVP; adding a +50KB dep at Phase B-late is wrong scope (operator can request the swap in v5.x+ once UX has stabilised)
- Heavy help (videos + AI chat) is shipped as scaffold ONLY — actual video recordings + AI backend land later; the API surface lets the rest of the wizard build against a stable contract
Decisions¶
7 locked decisions for the Phase B-late wave (BL1-BL7):
-
BL1 — Scan engine adapter pattern: Disabled / Fake / SSHReal triple (
scan-engine.ts).SSHRealScannerorchestrates 5 recipes per switch (interface-prepare → discovery-protocols-enable → show-mac-table → lldp-neighbors → snmp-walk-ifoper).interface-prepareis a HARD prerequisite (host fails);discovery-protocols-enableis SOFT-fail (falls back to MAC + ARP). PluggableSshExecutorinterface lets the Ansible-orchestration sidecar plug in without code changes. -
BL2 — OUI scraper pure-function + weekly CronJob:
oui-scraper.tsparses IEEE oui.csv stdlib-only (nocsv-parsedep). Sanity-gatemaxShrinkRatio=0.5guards against broken upstream feeds (last-known-good preserved). Weekly CronJob Sun 03:30 (after Saturday backup window); air-gap mode bypasses upstream fetch + logs skip. -
BL3 — Phase 4-7 panels are presentation layers: All 4 panels (capacity / prod-cabling / auto-config / stack-deploy) are pure React wrappers over already-tested lib functions (
computeCapacity,cabling.planCabling,connectivity-engine.aggregateVerdicts,deploy-plan.planDeploy). No new business logic — keeps test coverage concentrated in the libs. -
BL4 — YAML download via GET + Content-Disposition: Browser handles the file save via
?attachment=1query flag rather than client-sideBlob + URL.createObjectURL. Simpler client; works in air-gap. Optional git commit via POST +LAB_STAGING_GIT_BRIDGE_URLenv (503 + hint when missing — never silent failure). -
BL5 — react-flow swap STAYS DEFERRED: Q4 LOCKED in ADR-0095. HTML5 drag-drop UX shipped in WIZ-4 is sufficient. ADR-0096 logs this in carry-forward — operator may request when v5.x+ UX stabilises.
-
BL6 — ML affinity engine adapter pattern matches scan engine: Disabled / Fake / Onnx triple (
ml-affinity.ts).OnnxMLAffinityfalls back to heuristic suggester silently with confidence=0 when bridge unreachable — ML failure NEVER blocks the wizard (mirrors validator-art memo's locked behavior).pickMLEngineenv-driven;LAB_STAGING_ML_AFFINITY=fake|onnx+LAB_STAGING_ML_AFFINITY_URL. -
BL7 — Heavy help shipped as scaffold ONLY (Q1 v5.x+):
getVideoTutorialreturns null (empty catalog) until videos recorded;askAIChatthrowsErrAIChatNotWired. Feature flags viagetHeavyHelpFlags()default OFF + degrade gracefully to v5.0 Medium (CLI snippets).
Consequences¶
Positive¶
- Phase 2 connectivity engine is wired end-to-end: Operator can run scan against synthetic equipment today (Fake), against real equipment once the SSH bridge sidecar ships (SSHReal) — no wizard code changes needed at that point.
- OUI catalogue stays fresh without operator intervention: Weekly CronJob + IEEE upstream; air-gap installations get release-embedded fallback.
- All 7 phase panels are visible + interactive: Operator walks the wizard from Phase 1 to Phase 7; panels render verdict/status from libs; gating buttons enforce ADR-0095's locked decisions (T0 hard-floor; engineWired flags).
- YAML export round-trips deployments through git: Per Q5 LOCKED both paths; operator commits to bench repo; reproducible.
- ML affinity engine has a stable contract for v5.1 swap-in; OnnxMLAffinity fallback prevents ML failures from blocking the wizard.
- 271 Vitest tests (from 205 at end of ADR-0095) — +66 tests covering scan engine + OUI scraper + ML affinity + E2E + Heavy help stubs.
Negative¶
- ~~SSH bridge sidecar not yet shipped~~ ✅ Closed by post-WIZ-B5:
pkg/ssh-bridge-sidecar/Go service +k8s/oobi/73-ssh-bridge-sidecar.yaml(Deployment + Service + NetworkPolicy restricting ingress to Dashboard). Production operator setsLAB_STAGING_SCANNER=ssh+LAB_STAGING_SSH_BRIDGE_URL=http://ssh-bridge-sidecar.oobi-fabric.svc.cluster.local:8089. - ~~CLONER OUI CronJob image not yet built~~ ✅ Closed by post-WIZ-B5:
build/cloner-fn-oui-scrape/(Python stdlib script + Dockerfile + GHCR CI workflow);k8s/oobi/72-cloner-fn-oui-scrape.yamlnow points atghcr.io/nollagluiz/cloner-oui-scrape:v0.1.0. Built + pushed by.github/workflows/cloner-oui-image.yml. - react-flow still on the carry-forward list: Operator UX is functional but lacks zoom/pan/minimap.
- ML model not trained:
FakeMLAffinityconfidence is heuristic (based on input completeness, not real model output). Real training data pipeline lands when validator-ml-cortex Phase F retraining produces alab-affinity-v0.1ONNX export. - Heavy help videos not recorded: VIDEO_CATALOG is empty; flag degrades to v5.0 Medium silently.
Alternatives considered¶
- Ship react-flow swap in this wave: Rejected. Q4 LOCKED in ADR-0095 + +50KB dep mid-Phase-B-late is wrong scope. HTML5 drag-drop is functional.
- Embed real ML model training in WIZ-B5: Rejected. Training pipeline requires historical run data the bench doesn't have yet; FakeMLAffinity + OnnxMLAffinity stub is the right v5.1 shape.
- Record Heavy help videos now: Rejected per Q10 LOCKED (v5.x+ scope). Scaffolding the API surface is sufficient to unblock UI work.
- Wire SSH executor implementation inline: Rejected. The orchestration sidecar is a separate concern (cross-ref
discuss_ssh_telnet_orchestration_plane_2026_05_09); pluggableSshExecutorinterface keeps the wizard decoupled.
Compliance / governance¶
- CLAUDE.md invariant preserved: Dashboard-only operator interface — all 4 Phase B-late additions surface through
/admin/lab-staging+ API routes; no kubectl exec. - Audit trail extended: Phase 2 scan results land in
lab_deployments_historywithreason='oobi-scan'per Q6 LOCKED. - Air-gap support: OUI scraper respects
LAB_STAGING_AIRGAP=true; YAML export works without any external bridge; Heavy help scaffold degrades gracefully when flags off.
References¶
- Strategic memo:
discuss_lab_deployment_staging_2026_05_09 - ADR-0095 (wizard wave):
docs/ADR/0095-lab-deployment-staging-wizard.md - Recovery memo: project_lab_staging_phase_b_late_complete_2026_05_25.md
- Companion sidecar memo:
discuss_ssh_telnet_orchestration_plane_2026_05_09 - ml-cortex base: ADR-0088
- Topology axes: ADR-0011