GLOSSARY.md line-by-line audit. Eight corrections.
1. workspace-controller → environment-controller everywhere. The
controller reconciles the Environment CRD; "workspace" is banned as
a Catalyst scope, so it cannot be in a component name either. Fixed
in: GLOSSARY, ARCHITECTURE, PLATFORM-TECH-STACK, NAMING-CONVENTION,
SOVEREIGN-PROVISIONING, IMPLEMENTATION-STATUS, core/README,
BUSINESS-STRATEGY. Banned-term entry in GLOSSARY now explicitly
covers component names too.
2. "workspace repos" (per-Environment Gitea repos) → "Environment
Gitea repos" in GLOSSARY, PLATFORM-TECH-STACK.
3. JWT claim {workspace, org, role} → {environment, org, role} in
ARCHITECTURE projector diagram.
4. OpenOva definition refined: was "Never used to name a product",
which contradicted "OpenOva Catalyst", "OpenOva Cortex". Now: brand
prefix in product names; bare "OpenOva" = the company; bare
"Catalyst" = the platform.
5. Catalyst definition completed: was missing provisioning, billing,
gitea, observability — now lists all 14 control-plane components,
pointing at the table below.
6. Catalyst components table: added `provisioning` (validates
configSchema, commits to Environment Gitea); reordered to match
ARCHITECTURE §3 grouping; clarified each component's source-of-truth
(catalog-svc reads monorepo + Gitea, blueprint-controller watches
monorepo + Gitea, etc.).
7. Environment definition: refers to NAMING §2.4 for env_type values;
removed inline list that didn't match canonical ordering. Added
concrete examples (acme-prod, acme-dev, bankdhofar-uat).
8. Application example: dropped "RocketChat" which appeared nowhere
else; replaced with generic "running deployment" plus the
established WordPress / Postgres examples.
9. sovereign-admin description: was "runs Crossplane" — Crossplane is
platform plumbing not user-facing. Now: "manages the underlying
clusters via Crossplane (which is platform plumbing, not a
user-facing surface)".
Banned-term coverage:
- "Workspace" entry now covers BOTH the Catalyst scope AND component
naming (workspace-controller → environment-controller).
Refs #37
First validation iteration. Three concrete corrections.
1. Add docs/IMPLEMENTATION-STATUS.md as the bridge between target
architecture and current code state. Status legend (✅ / 🚧 / 📐 / ⏸)
applied per-component. Catalyst control plane = mostly 📐. Component
READMEs = 🚧 (README only, no Blueprint manifests yet). products/axon
= ✅ (only product with real code). core/ = 📐 (just .gitkeep).
2. Status banner added to ARCHITECTURE, SECURITY, SOVEREIGN-PROVISIONING,
BLUEPRINT-AUTHORING, PERSONAS-AND-JOURNEYS, PLATFORM-TECH-STACK, SRE
pointing readers at IMPLEMENTATION-STATUS.md before they treat any
described feature as built. GLOSSARY also references it.
3. Architectural decision (Option A — monorepo canonical):
- Each platform/<name>/ and products/<name>/ folder is the source of
ONE Blueprint, published as ghcr.io/openova-io/<name>:<semver> by
CI fan-out from the monorepo root.
- BLUEPRINT-AUTHORING.md §1, §2, §13 rewritten to match.
- README.md "what's in this repo" rewritten to clarify monorepo +
OCI-fan-out shape; no longer claims every directory is a Blueprint
in a way that contradicts BLUEPRINT-AUTHORING.
Wrong-org fixes (3 places):
- docs/PERSONAS-AND-JOURNEYS.md:13 github.com/openova → openova-io
- docs/BLUEPRINT-AUTHORING.md:13 github.com/openova → openova-io
- docs/BLUEPRINT-AUTHORING.md:404 github.com/openova → openova-io
- docs/BLUEPRINT-AUTHORING.md ghcr.io/openova/* (3 refs) → openova-io
API group consistency:
- All references unified to catalyst.openova.io/v1alpha1
(was mixed v1 / v1alpha1; v1alpha1 is correct since the CRDs are
design-stage with no implementation).
core/README.md updated to honestly describe the directory tree as
"target structure with .gitkeep placeholders" rather than implying
the apps/console, apps/projector, etc. binaries already exist.
The legacy apps/bootstrap and apps/manager directories are
acknowledged as transitional placeholders that will be removed when
the new apps/ layout is scaffolded.
CLAUDE.md and .claude/project-memory.md updated to put
IMPLEMENTATION-STATUS.md second in the read-first ordering.
Refs #37
Targeted updates to BUSINESS-STRATEGY.md §5.1 and §9.2 plus
TECHNOLOGY-FORECAST §removed-components.
- BUSINESS-STRATEGY.md §5.1: OpenOva Catalyst row repositioned. It is
the platform itself (the self-sufficient Kubernetes-native control
plane that turns any cluster into a Sovereign), not a sub-product
bundling bootstrap+IDP+lifecycle manager. Other OpenOva products
(Cortex, Fingate, Fabric, Relay, Specter, Axon) run ON Catalyst as
composite Blueprints.
- BUSINESS-STRATEGY.md §9.2: capability matrix "Developer portal" cell
updated from "Catalyst IDP" to "Catalyst console" — IDP function is
one of the console's responsibilities, not a separate product.
- TECHNOLOGY-FORECAST.md §removed-components: Backstage row updated to
describe replacement as "Catalyst console (the platform's own
developer-facing UI)" rather than the now-retired "Catalyst IDP"
sub-product.
Strategy narrative, market segmentation, pricing model, and migration
playbook are unchanged — they stand on their own.
Refs #37
Two related rewrites that put the control plane / application Blueprint
distinction front and center.
PLATFORM-TECH-STACK.md
- §1: explicit three-way component categorization — Catalyst control
plane (one per Sovereign), per-host-cluster infrastructure (every
cluster), Application Blueprints (inside per-Org vclusters).
- §2: Catalyst control plane components listed by responsibility —
user-facing surfaces, backend services, identity, secrets, event
spine, GitOps, networking, security, scaling, storage,
observability, resilience.
- §3: Application Blueprints (the a-la-carte catalog) — Valkey and
Strimzi explicitly callout that they are Application Blueprints,
NOT control-plane components (control plane uses NATS JetStream).
- §4: composite Blueprints (Cortex, Axon, Fingate, Fabric, Relay)
repositioned as Applications running ON Catalyst, not as parallel
products.
- §5: multi-region diagram showing independent OpenBao Raft per
region, NATS leaf nodes, Crossplane on mgt.
- §6: resource estimates updated for control plane (~12 GB +
per-Org Keycloak in SME tier).
- §10: license posture table — every control-plane component carries
a redistribution-safe license (no BSL).
SRE.md
- §2: multi-region principles updated; explicit "no stretched
clusters" applies to OpenBao, JetStream, etcd, every quorum-
based component.
- §2.5: data replication patterns now scoped to Application
Blueprints (the things a customer installs), separate from
control-plane patterns documented in SECURITY.md and
ARCHITECTURE.md.
- §4: alert-to-action mapping segmented by Catalyst control plane
vs per-product (Cortex, Fingate); new alerts: OpenBaoSealed,
JetstreamLagHigh.
- §7-§13: terminology aligned to Catalyst (console instead of IDP);
runbooks now Runbook CRD-backed; incident severities updated.
- §13.2-13.3: Catalyst-specific incidents (workspace-controller,
OpenBao seal, projector lag) plus AI Hub incidents under
bp-cortex installation.
Refs #37
Repositions the public repo's identity. OpenOva is the company; Catalyst
is the platform. Sovereign is a deployed Catalyst. The historical
positioning (OpenOva = platform, Catalyst = bootstrap+IDP+lifecycle
sub-product) is retired. Catalyst now subsumes bootstrap, lifecycle, and
IDP responsibilities into one control plane.
- README.md Catalyst-first front door. Sovereign concept,
repo structure, stack at a glance, cloud
provider matrix, getting-started paths
(managed via marketplace.openova.io vs
self-host via catalyst-provisioner).
- CLAUDE.md Codebase guide for Claude. Banned-term table,
commit conventions (hatiyildiz default for
public repo), the no-fourth-surface rule,
per-component README rule of thumb.
- .claude/project-memory.md Reduced to an index + decision log;
full architecture moved to docs/. Stack
decisions locked (NATS JetStream, OpenBao,
SPIFFE/SPIRE, per-Org Keycloak SME / per-
Sovereign corporate, Crossplane only IaC,
no Terraform/Pulumi user-facing surface).
- core/README.md Catalyst control-plane Go application. Drops
the bootstrap-vs-manager split (both fold under
"Catalyst control plane"). Lists each component
deployable from this codebase: console,
marketplace, admin, projector, catalog-svc,
provisioning, workspace-controller, blueprint-
controller, billing. CRD list updated:
Sovereign / Organization / Environment /
Application / Blueprint / EnvironmentPolicy /
SecretPolicy / Runbook.
Refs #37
The naming convention pre-dates vcluster and Catalyst's user-facing
Environment object. Three additions, one rename:
- §2.4: {env} dimension renamed to {env_type} to disambiguate from the
Catalyst Environment object (which is the user-facing scope, not a
dimension).
- §2.5: new Organization dimension (slug, lowercase, hyphenated). Used
for vcluster identity and any Organization-scoped resource.
- §4.7: new vcluster naming layer. Pattern is just {org} within the
parent host cluster (Don't Repeat the Parent — Principle 1.2). Globally-
qualified form is {prov}-{reg}-{bb}-{env_type}-{org} for cross-cluster
references and kubeconfig contexts.
- §11: Catalyst Environment defined as the user-facing {org}-{env_type}
scope. One Environment is realized by N vclusters across regions × bb
filtered by Application Placement. Each Environment has its own Gitea
repo and JetStream Account.
Tags updated: openova.io/environment → openova.io/env-type for
disambiguation; new openova.io/organization, openova.io/vcluster,
openova.io/environment (for Catalyst scope), openova.io/sovereign tags.
DNS pattern §5 split into two: control-plane (component.{location-code}.
{sovereign-domain}) and Application (app.{environment}.{sovereign-or-org-
domain}) — supporting white-label Sovereigns where the Application DNS
uses the customer's own domain.
Refs #37
Client sends `thinking: true` to enable reasoning tokens. Default remains
disabled for instant streaming.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Qwen3-coder generates hundreds of `reasoning` tokens before `content`
tokens, causing 10+ second perceived delay. The reasoning tokens stream
through Axon but the ChatWidget only renders `delta.content`, so users
see a long pause then a burst. Passing `enable_thinking: false` via
chat_template_kwargs skips the reasoning phase entirely.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3-turn conversations passed at ~9120 chars but 4-turn failed at ~10640.
WAF anomaly threshold is between those values. Lowered all limits to keep
multi-turn conversations well under the threshold.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WAF anomaly scoring accumulates across the entire request body. After 2-3 turns,
assistant responses containing infrastructure terms (security, scanning, etc.)
push the total past the threshold. Added per-assistant trim (1500 chars) and a
12000-char sliding window that drops oldest messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
vLLM requires system messages to be at the beginning. When Axon merges
conversation history with new messages, duplicate system messages cause
a 400 error. Strip all but the first system message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The vLLM backend at Bank Dhofar runs behind an Istio/Envoy WAF with
ModSecurity-style anomaly scoring. The ChatWidget's 41KB system prompt
accumulates enough infrastructure/security keywords to trigger a 403.
Trim system messages to 6000 chars (70% head + 30% tail) before
forwarding to vLLM — preserves identity/behavior instructions at the
start and FAQ/response guidelines at the end.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clients (e.g. ChatWidget) send OpenAI model names like gpt-4o-mini which
vLLM doesn't recognize. The provider now queries available models on
startup and remaps any unrecognized name to the configured default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces a provider abstraction so Axon can proxy to either Claude SDK
(existing behavior) or a vLLM-compatible endpoint. Toggled via
AXON_PROVIDER env var ("claude" | "vllm"). When vllm, requests pass
through as-is (no prompt translation), session pool and OAuth are skipped.
Closesopenova-io/openova#36
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Part of the brand-consistency sweep (openova-private#116). All brand
surfaces must use the swirl mark with gradient #3B82F6 → #818CF8.
- public/favicon.svg: replaced with canonical mark (was a purple
placeholder logo)
- OOLogo.tsx: default c1 coerced from #38BDF8 → #3B82F6 to match the
brand/logo-mark.svg canonical gradient
- AuthLayout.tsx: OctagonAlert in blue-square placeholder → OOLogo;
label 'OpenOva Corporate' → 'OpenOva Sovereign'
- AppLayout.tsx: same — OctagonAlert → OOLogo; 'Corporate' → 'OpenOva Sovereign'
Part of the tier-URL cutover (openova-private issue #116). Catalyst UI moves
from catalyst.openova.io to console.openova.io/sovereign.
- vite.config.ts: base '/sovereign/' so built HTML/asset refs are prefixed
- src/app/router.tsx: TanStack Router basepath '/sovereign' so Link/navigate
emit /sovereign-prefixed URLs
- src/shared/config/urls.ts (NEW): central BASE / API_BASE / path() helper
- StepReview.tsx: fetch('/api/v1/deployments') → fetch(`${API_BASE}/v1/deployments`)
and window.location.href = '/provision.html' → path('provision.html')
- StepCredentials.tsx: same treatment for /api/v1/credentials/validate
- nginx.conf SPA fallback simplified to try_files $uri /index.html — avoids
nginx's directory trailing-slash redirect that would strip the /sovereign
prefix client-side
No more hardcoded URLs in the source (see feedback_never_hardcode_urls rule).
Footer used to be rendered inside StepShell — every step navigation
unmounted the old footer and mounted a new one, which produced a
visible flicker (the 'bounce' the user reported).
Architecture change:
- New shared/lib/wizardNav.ts — tiny Zustand store holding the active
step's nav handlers (onNext, onBack, disabled, loading, label, title)
- StepShell now publishes its nav state via useEffect; renders no footer
- WizardLayout renders ONE persistent footer that reads from wizardNav
Result: footer DOM is stable across step transitions. Buttons swap
behavior synchronously (no remount, no fade-in flicker). Stepper
counter and progress pill stay in place too.
User: 'I don't think anyone will read that section. If you believe
the info is required, put it at the bottom.'
Matching SME's pattern now:
- Compact 1.1rem title with subtle border-bottom divider
- Description paragraph removed from the top
- Description re-rendered at the BOTTOM of the step content as muted
helper text (0.82rem, dashed border-top), so users who want the
context can still read it after completing the main task
- Reclaims ~70-90 px of vertical space above the step content
User requested Corporate inherit every polish element where SME is
better. Changes (all minimum-touch):
Palette (globals.css):
- Accent channel: sky-400 (56,189,248) → SME blue-500 (59,130,246)
- Light-mode accent: sky-600 (2,132,199) → SME blue-600 (37,99,235)
- New --wiz-success-ch: SME emerald (16,185,129 dark / 5,150,105 light)
- Unifies green dots and blue pills with SME on sight
WizardLayout.tsx:
- Stepper circle 'done' state: #22C55E → rgba(var(--wiz-success-ch))
- Stepper separator 'done': same
- Driven entirely by CSS vars — light/dark flips automatically
_shared.tsx (StepShell flat + SME footer):
- Removed outer card wrapper (no bg, no border, no shadow, no blur)
- Content flows flat — child cards are the only surfaces now
- Nav buttons moved to a sticky bottom footer (like SME):
• Back: transparent outline, SME border style
• Continue: solid SME accent, subtle shadow, no gradient
- Backdrop-blur on footer matches the header
- Loading spinner inline in Continue button
User preferred the previous approach — stepper as its own centered
row below the header, matching SME's current pattern exactly.
Reverts the in-header stepper change; restores WizardLayout.tsx to
the state from commit 7ed9239.
User wanted the stepper to live in the header area to reclaim
vertical space, not as a separate row below. Now:
- Header: 3-zone grid (logo · stepper · actions) in one row
- Stepper: inline pills (row: circle + label side-by-side)
- Active pill has accent bg + ring; done shows green circle with check
- Responsive: labels hide below 980px, pill strips to compact dots
- Phone: header reflows to 2 rows (logo/actions + stepper below)
- All chrome fits in ~56 px of header height total
User request: unify both wizards on the horizontal pattern and bring
Corporate in line with SME's look-and-feel (dark/light mode, colors,
cards) with minimum changes.
Minimum-touch changes:
- globals.css: flatten --wiz-page-bg from radial gradient to solid
#0b1220 (dark) / #f8fafc (light) — matches SME's flat bg.
--wiz-panel-bg bumped to #111827 (dark) / #ffffff (light) to match
SME card surfaces.
- WizardLayout.tsx: complete rewrite as a horizontal top-stepper
(header + stepper row + content), mirroring the SME stepper pattern
(32px numbered circles + labels below + 44px connecting lines).
Done circles turn green with a check; active is accent blue with a
soft ring; pending stays as a hollow circle.
- Responsive: labels hide below 720px, circles shrink to 28px so 6
steps remain legible on tablets and phones.
Step content components (StepOrganisation, StepTopology, ...) are
unchanged — they inherit the new palette via the existing --wiz-*
variables.
User feedback: 1km gap between balls and main card, and vertical spacing
between balls was too tight at 22px.
- Body padding-left 40px → 8px (desktop)
- Content wrapper: margin: 0 auto → marginLeft: 0 (left-align to hug
the sidebar; card right edge now rests against the balls)
- Desktop step gap: 22 → 28 (+27%)
- Tablet step gap: 18 → 24 (+33%)
- Content maxWidth 960 → 1000 to fill the extra breathing room
User feedback: previous revision brought back a subtle sidebar pane
(tint + right border) which was wrong direction. Also gaps between
balls stretched to fill full viewport height, making spacing excessive.
Redesign:
- Sidebar width 260 → 200 px, NO bg, NO border (fully transparent)
- Fixed 22 px gap between balls — no more flex:1 stretch
- Stepper right-aligned within sidebar so balls sit flush against
the main content card (tight visual proximity, as requested)
- Labels rendered LEFT of balls (one word each — dropped the
two-line title+description pattern)
- Logo also right-aligned to match direction
- Progress bar compact at the bottom, right-aligned
- Tablet variant: icon-only balls, same transparent + centered pattern
Previous redesign killed sidebar bg entirely — content read as left-aligned
because the left 260px was visually empty (no counterbalance).
Also: pending rails used --wiz-border-sub (rgba 255/0.06) for a dashed
pattern, which rendered as invisible. User reported 'no lines between
balls when not selected'.
Fixes:
- Sidebar: subtle tint rgba(var(--wiz-ch), 0.015) + thin right border
rgba(var(--wiz-ch), 0.08). Enough weight to balance the page without
returning to 'menu' feel.
- Rail thickness: 1.5px → 2px for cleaner rendering
- Pending rail: solid rgba(var(--wiz-ch), 0.2) instead of invisible
dashed. Always visible regardless of state.
- Border radius 1px on rails for softer edges.
- Applied consistently to desktop and tablet variants.
Sidebar:
- Removed distinct bg + border + backdrop-filter that made it read as a menu
- Added vertical connecting rail between step circles (solid gradient for
done/current, dashed grey for pending) — clearly signals journey, not nav
- Distributed steps with flex: 1 grow on each item so the rail fills
the full viewport height instead of clustering at top
- Active step circle has a soft pulse ring animation
- Progress bar integrated at rail's end (no hard divider)
- Same rail pattern applied to tablet variant
Rename (user-facing only — internal codename stays "catalyst"):
- index.html title: OpenOva Catalyst → OpenOva Corporate
- WizardLayout logo sub-label: Catalyst → Corporate
- AuthLayout brand text: OpenOva Catalyst → OpenOva Corporate
- AppLayout sidebar label: Catalyst → Corporate
- LoginPage subtitle: "Catalyst account" → "Corporate account"
Not renamed (internal): store names, CSS vars, repo paths, k8s namespace,
catalyst.openova.io domain — avoids SEO/DNS/infra churn.
Replace the live-SSE phase+log view with a static DAG animation page
at /provision.html. Launch OpenOva now redirects there via
window.location. The old React ProvisionPage and /provision route are
removed. Backend POST /api/v1/deployments still fires so the API side
is unchanged; only the rendered provisioning view is swapped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skip refresh gracefully when .credentials.json doesn't exist (e.g. CI
smoke test with no Claude auth mounted).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Claude Agent SDK does not refresh OAuth tokens. Axon now:
1. Refreshes the token on startup before creating session pool
2. Runs a periodic refresh every 4 hours
3. Writes refreshed credentials to disk so session subprocesses use them
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Claude Agent SDK does not handle OAuth token refresh. Adds a CronJob
(every 4h) that refreshes the token via Anthropic's OAuth endpoint and
updates the K8s secret. Disabled by default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Claude Agent SDK does not handle OAuth token refresh — it reads the
accessToken from .credentials.json and uses it directly. When the token
expires (~8h), Axon returns 401 until manually refreshed.
Adds a CronJob (every 4h by default) that:
1. Reads the refreshToken from the K8s secret
2. Calls Anthropic's OAuth token endpoint to get a fresh accessToken
3. Updates the K8s secret with the new credentials
4. Restarts the Axon deployment to pick up the new token
Includes ServiceAccount, Role, and RoleBinding for least-privilege access.
Disabled by default (axon.tokenRefresh.enabled: false).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The credentials were mounted as a read-only K8s secret subPath. When the
Claude SDK refreshed the OAuth token, it couldn't persist the new token
back to disk. On pod restart, the stale expired token was loaded again,
causing 401 auth failures.
Fix: initContainer copies credentials from secret to a writable emptyDir
volume. The SDK can now refresh tokens and persist them within the pod
lifecycle. Also creates the debug/ directory the SDK requires.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Valkey was crash-looping (372 restarts) because the 521MB RDB exceeded
the 512Mi memory limit. Adds maxmemory and maxmemory-policy args to
the valkey deployment template with configurable defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all rgba(sch,opacity) template literals in the sidebar with
semantic --wiz-text-* CSS variables that are pre-calibrated for WCAG AA
in both dark and light mode:
rgba(sch,0.85) → --wiz-text-hi (21:1 on light)
rgba(sch,0.45) → --wiz-text-md (9.5:1 on light, was 3.3:1 — FAILED)
rgba(sch,0.20) → --wiz-text-sub (4.7:1 on light, was 1.5:1 — FAILED)
rgba(sch,0.05) → --wiz-bg-input
rgba(sch,0.10) → --wiz-border
rgba(sch,0.06) → --wiz-border-sub
rgba(sch,0.08) → --wiz-bg-input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The left nav sidebar was forced dark (#0c1525→#0f172a gradient) in light
mode via a hardcoded LIGHT_SIDEBAR_BG constant. Remove it entirely and let
`--wiz-bg-sub` (#f4f6f8) and `--wiz-ch` (0,0,0 channel) take over, giving
proper light text-on-light-bg contrast via the existing CSS variable system.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Line 1: product name left-aligned + M/R/O color badges right-aligned
Line 2: conceptual product family subtitle (GitOps & IaC, Networking & Service Mesh, etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Component mini-cards: M/R/O color badges on first line, product name
(PILOT, SPINE, etc.) on second line — numbers lead, label follows
- Infrastructure section: topology name and AIR-GAP badge moved into
the section header bar — eliminates the separate summary block and
its whitespace; region cards start immediately below the header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Each option card: explicit height:62px + overflow:hidden — guaranteed
equal height for all 5 regardless of tagline length
- Tagline: whiteSpace:nowrap + textOverflow:ellipsis — 1 line max,
full text visible in the detail panel on the right
- Outer container: alignItems flex-start — right panel height changes
on selection no longer affect left panel (root cause of jumping)
- Canvas: fixed height:200px — substantial diagram, consistent size
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TopologyDetail canvas: flex:1 so SVG grows to fill all space between
header and bullets — eliminates bottom whitespace
- TopologyDetail bullets: flexShrink:0 (fixed at bottom, doesn't flex)
- Legend font size: 8px → 11px, dot size 8px → 10px, better readability
- Left panel options: CSS grid repeat(5,1fr) so all 5 rows are equal
height on desktop — tallest item sets the row height for all five
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. StepTopology — fixed-height diagram canvas (152px) with SVG height=100%; all topologies
render at identical visual size regardless of viewBox aspect ratio
2. StepTopology — outer container alignItems stretch; left panel and right panel bottoms
always aligned consistently
3. StepTopology — air-gap toggle no longer expands; pure toggle with no diagram overlay
4. StepProvider — max 3 region cards per row, wraps to 2nd line for CITADEL (4 regions);
when air-gap enabled, amber-styled air-gap region card appended
5. StepReview — redesigned as 2-row layout:
- Row 1: Organisation (1fr) + Components (2fr) — 3x3 group mini-cards with M/R/O counts
- Row 2: Infrastructure full-width — topology/airgap summary + flex region cards
(1-5 cards, all equal height via alignItems stretch)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StepTopology: add VBOX helper — dashed inner border + vC badge on every
physical cluster box; shows 3 vClusters (MGMT/DMZ/RTZ) inside SOLO and
COMPACT clusters; 1 vCluster per physical cluster in ZONED/DUAL/CITADEL
- Add vclusters count stat to topology option list and detail panel
- Add vCluster legend below each diagram explaining dashed border semantics
- componentGroups: add vcluster (mandatory) to PILOT group
- componentGroups: replace MinIO with SeaweedFS (multi-protocol) in SILO group
- model: update DEFAULT_COMPONENT_GROUPS pilot + silo defaults accordingly
- store: migrate persisted state — minio → seaweedfs, inject vcluster into pilot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Architecture:
- Add CITADEL topology — 4 regions, 6 clusters: 2 dedicated CP regions
(MGMT-only, HA pair, no workload) + 2 DP regions (DMZ+RTZ each); replaces
the broken TRIANGLE which had single-site MGMT; Tier-1 Bank tag
- Keep DUAL (6 clusters, 2 regions) as recommended Enterprise option
- Topology range: CITADEL (4r/6c) → DUAL (2r/6c) → ZONED (2r/4c) →
COMPACT (2r/2c) → SOLO (1r/1c)
- Register 'citadel' in TopologyTemplate, TOPOLOGY_REGION_COUNT (4),
TOPOLOGY_REGION_LABELS (CP1/CP2/DP1/DP2), and store sanitize list
Cosmetic:
- Diagram area background changed from rgba(0,0,0,0.25) grey to a proper
dark canvas (linear-gradient #0a1628→#0f172a) so topology SVG colours
pop in both light and dark modes — no more passive greyed-out look
- SVG helpers use hardcoded white fill/stroke instead of CSS vars so the
diagrams are always crisp on the dark canvas regardless of theme
- Default topology reset to null (no pre-selected value)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove TRIANGLE topology (single-site MGMT is architecturally unsound);
DUAL (6 clusters) is now the top option, MGMT active in both regions;
range is now 6→4→2→1 building blocks (DUAL, ZONED, COMPACT, SOLO)
- Topology step: equal-height left/right columns via alignItems:stretch +
flex:1 on each option card so all cards share the same height
- Provider step: region cards always fill 100% width (Array(n).fill('1fr'));
remove 420px single-region cap; remove 'Credentials required next' card
- Provider dropdown: fix active-item text (#fff→var(--wiz-text-hi)) so it
reads on white panel in light mode; lighten panel shadow for light mode
- Add --wiz-accent CSS token (dark: rgba(56,189,248,0.8) / light: #0284c7
WCAG AA ✓); replace all rgba(56,189,248,0.X) text colours across every
wizard step so they are readable in light mode
- Review step: rename button to 'Launch OpenOva'
- --wiz-panel-bg in light mode → #f1f5f9 so dropdown panel is distinct
from surrounding white card background
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>