Commit Graph

284 Commits

Author SHA1 Message Date
e3mrah
00ddc1437c fix(axon): cap assistant messages and total payload to prevent WAF rejection on long conversations
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>
2026-04-26 08:44:33 +02:00
e3mrah
40c4abe4f6 fix(axon): deduplicate system messages before forwarding to vLLM
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>
2026-04-26 08:35:28 +02:00
e3mrah
4110161577 fix(axon): trim large system prompts to avoid vLLM WAF rejection
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>
2026-04-26 08:27:14 +02:00
e3mrah
85e1319e01 fix(axon): resolve unknown model names to vLLM default
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>
2026-04-26 07:54:07 +02:00
e3mrah
68fcbe1aed feat(axon): add toggleable vLLM provider backend
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.

Closes openova-io/openova#36

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-26 07:36:58 +02:00
e3mrah
500aca483e refactor(catalyst/ui): canonical OpenOva mark everywhere; drop OctagonAlert placeholder
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'
2026-04-22 09:08:20 +02:00
e3mrah
60938c0775 refactor(catalyst/ui): serve at /sovereign base path; centralize URL config; never hardcode
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).
2026-04-21 18:31:08 +02:00
e3mrah
5dfb4f3867 fix(catalyst): persistent footer (no more bounce on step change)
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.
2026-04-16 12:44:16 +02:00
e3mrah
09ed921b81 feat(catalyst): compact step header + bottom-placed helper text
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
2026-04-16 11:58:04 +02:00
e3mrah
b09503cdf9 feat(catalyst): align light borders + useful footer
1) Light-mode palette alignment with SME:
   - --wiz-border #d1d5db → #e2e8f0 (SME slate-200)
   - --wiz-text-md #334155#475569 (SME text-dim)
   - --wiz-border-sub adjusted to SME slate-100
   Fixes the darker-border-in-light-mode discrepancy the user spotted.

2) Sticky footer now uses its space. Adds a left-hand summary:
     'Step 3 of 6 · Provider' + progress pill (33%)
   Matches SME's summary-on-left pattern.
   Responsive: pill hides <820px, title/divider hide <640px.
2026-04-16 11:35:46 +02:00
e3mrah
3a836a1884 feat(catalyst): deep unification with SME — colors, buttons, footer, flat cards
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
2026-04-16 10:51:28 +02:00
e3mrah
427023cf6a revert(catalyst): restore stepper-below-header (previous approach)
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.
2026-04-16 10:27:34 +02:00
e3mrah
3bb3666e09 fix(catalyst): integrate stepper INTO header row
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
2026-04-16 09:50:06 +02:00
e3mrah
7ed9239c2d feat(catalyst): unify wizard with SME — horizontal stepper, flat palette
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.
2026-04-16 08:57:34 +02:00
e3mrah
afd9df01de fix(catalyst): balls adjacent to content card + +30% vertical gap
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
2026-04-16 08:46:20 +02:00
e3mrah
7fe0133b9e fix(catalyst): strip pane, cap gap, labels-left, right-aligned stepper
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
2026-04-15 23:04:22 +02:00
e3mrah
403d5083c2 fix(catalyst): sidebar balance + visible pending rails
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.
2026-04-15 22:51:39 +02:00
e3mrah
9e344078a2 feat(catalyst): wizard sidebar redesign + rename UI to "Corporate"
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.
2026-04-15 22:38:54 +02:00
e3mrah
7a9f308eb5 feat(catalyst): swap provisioning screen to DAG visualization
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>
2026-04-14 22:48:13 +02:00
e3mrah
dd2e9b1de3 fix(axon): handle missing credentials file in token refresh
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>
2026-03-30 15:08:28 +02:00
e3mrah
0cfe1bc361 feat(axon): add OAuth token refresh on startup and periodic timer
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>
2026-03-30 15:07:07 +02:00
e3mrah
2da38e9f7a feat(axon): CronJob for automatic OAuth token refresh
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>
2026-03-30 14:44:40 +02:00
e3mrah
9a878336e3 revert: remove accidentally committed untracked files 2026-03-30 14:44:16 +02:00
e3mrah
52358eb8e7 feat(axon): CronJob for automatic OAuth token refresh
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>
2026-03-30 14:42:51 +02:00
e3mrah
a33cd5f9d3 fix(axon): writable credentials mount for OAuth token refresh
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>
2026-03-30 14:38:10 +02:00
e3mrah
361db09507 fix(axon): add valkey maxmemory config to prevent OOM crash loop
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>
2026-03-25 06:37:42 +01:00
Emrah Baysal
47160df77e fix(catalyst): sidebar text contrast — use --wiz-text-* tokens (WCAG AA)
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>
2026-03-21 21:12:54 +01:00
Emrah Baysal
5c2830ab6f fix(catalyst): light sidebar in light mode — remove Midnight Split dark override
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>
2026-03-21 20:49:37 +01:00
Emrah Baysal
92eeae9977 fix(catalyst): component cards — name+counts on line 1, subtitle on line 2
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>
2026-03-21 20:18:06 +01:00
Emrah Baysal
6cf42a63f4 fix(catalyst): review — counts on line 1, name on line 2; infra header compact
- 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>
2026-03-21 19:37:44 +01:00
Emrah Baysal
5de41c9c7b fix(catalyst): option cards fixed height; decouple panels to stop jumping
- 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>
2026-03-21 19:35:34 +01:00
Emrah Baysal
ef256fde4f fix(catalyst): topology detail fills card height; option rows equal height
- 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>
2026-03-21 19:16:36 +01:00
Emrah Baysal
f1750cb1f4 feat(catalyst): UX polish — fixed canvas, aligned panels, air-gap card, review redesign
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>
2026-03-21 19:06:05 +01:00
Emrah Baysal
30a5f1f072 feat(catalyst): redesign topology diagrams + review layout cleanup
- SOLO/COMPACT: 1 cluster per row, 3 vClusters as horizontal columns (DMZ|RTZ|MGMT)
- ZONED: row 1 = full-width DMZ cluster; row 2 = RTZ+MGMT as 2 vClusters in 1 PC; vclusters 4→6
- CITADEL: fix text/line overlap by increasing DP→CP gap to 16px; shorten CP region labels
- Topology option list: alignSelf flex-start — stops panel jumping when detail changes height
- Default topology: null → zoned
- StepReview: remove Credentials section; 3-card layout with stretch alignment and flex-1 fill;
  compact Infrastructure rows; Section accepts optional style prop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:19:28 +01:00
Emrah Baysal
0c0a982c1d feat(catalyst): nested vCluster boxes, network ordering, AIR-GAP add-on
Topology diagrams:
- Redesign all SVGs — outer box = physical cluster (PC helper), inner
  coloured box = vCluster (VC helper); true nested visual hierarchy
- Network mindset ordering enforced top→bottom: DMZ · RTZ · MGMT in all
  stacked layouts (SOLO, COMPACT, ZONED, DUAL)
- CITADEL: DP regions (DMZ over RTZ) on top, CP regions (MGMT-only) at bottom
- ZONED: changed from side-by-side to top-bottom stacking (DMZ top, MGMT·RTZ bottom)
- Colour legend added below each diagram

AIR-GAP add-on:
- AirgapAddon component in StepTopology — amber toggle card below topology list
  shows +1 region/cluster/vC, collapsible diagram, pull-only annotation
- DiagramAirgap SVG showing abstract topology → pull-only arrow → isolated AIR-GAP region
- model.ts: airgap: boolean field added to WizardState + INITIAL_WIZARD_STATE
- store.ts: setAirgap action
- StepReview: AIR-GAP row in Infrastructure section + TOPOLOGY_NAMES updated
  (removed triangle, added vCluster counts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 17:24:49 +01:00
Emrah Baysal
6e314c2c54 feat(catalyst): vCluster isolation layer in topology + SeaweedFS replaces MinIO
- 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>
2026-03-21 17:08:51 +01:00
Emrah Baysal
1dfaf1f00e feat(catalyst): restore CITADEL topology + dark diagram canvas
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>
2026-03-21 11:24:39 +01:00
Emrah Baysal
d2821592fc fix(catalyst): wizard UX — topology, provider, review, accent colour system
- 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>
2026-03-21 11:02:59 +01:00
Emrah Baysal
340821d19d feat(catalyst): redesign review step + fix topology/component wizard bugs
- StepReview: add cloud provider logos to Infrastructure and Credentials rows,
  replace boring "X selected" with M/R/O color-coded tier breakdown chips,
  rename button to "Launch OpenOva ecosystem", update step title/description
- componentGroups.ts: extract shared GROUPS data (Tier/ComponentDef/GroupDef)
  so StepComponents and StepReview both import from a single source of truth
- StepComponents: import GROUPS from componentGroups.ts, fix Zustand stale
  state bug (use getState() after toggleGroupComponent to read live state,
  eliminating Livekit→Stunner mis-selection and similar Fabric group issues)
- StepTopology: fix selected item text turning white on light background
  (isSelected color: '#fff' → 'var(--wiz-text-hi)' which is #0f172a in light)
- WizardLayout: remove A/B/C light mode picker, lock in Option B (Midnight
  Split dark sidebar) permanently as LIGHT_SIDEBAR_BG constant

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:57:16 +01:00
Emrah Baysal
9c8258b5c8 fix(catalyst): replace opacity-based wiz-ch text tokens with WCAG AA semantic tokens
All `rgba(var(--wiz-ch),X)` patterns replaced with named semantic tokens
across all 7 wizard step components + WizardLayout:

- --wiz-text-hi / md / lo / sub / hint — text tiers with WCAG AA light values
- --wiz-border / --wiz-border-sub — border tiers
- --wiz-bg-card / input / sub / xs — background tiers
- --wiz-card-shadow — eliminates the 0 32px 80px rgba(0,0,0,0.5) blotch in light mode

Light mode values: #0f172a (21:1) → #64748b (4.7:1 AA). Dark mode behaviour
unchanged — tokens resolve to same rgba(255,255,255,X) values as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 22:27:11 +01:00
Emrah Baysal
c80a3a6cc8 feat(catalyst): add A/B/C light mode option picker for UAT
Floating A·B·C toggle appears top-right in light mode only.
A = Slate Pro (cool gradient), B = Midnight Split (dark sidebar),
C = Cloud Paper (white sidebar + slate bg). Persisted in localStorage.
Option B properly restores dark sidebar with white channel text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:56:17 +01:00
Emrah Baysal
32325e23d9 fix(catalyst): light mode — replace all hardcoded rgba(255,255,255,X) with CSS channel variables
Introduce --wiz-ch CSS channel variable (255,255,255 dark / 0,0,0 light) and
matching --wiz-page-bg, --wiz-panel-bg, --wiz-deep-bg overrides in
[data-theme="light"]. Replace every hardcoded rgba(255,255,255,X) inline style
across WizardLayout, all 7 step components, and _shared with
rgba(var(--wiz-ch),X) so text, borders, and surface tints automatically invert
when the theme toggles. Fix SVG diagram helpers (BOX/REGION) to use style={{}}
attributes instead of presentation attributes so CSS custom properties resolve
correctly. Replace hardcoded dark panel backgrounds (#131320, #0f1117) with
var(--wiz-panel-bg) / var(--wiz-deep-bg).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:21:27 +01:00
Emrah Baysal
2611152792 fix(catalyst): remove unused fg param in Badge to satisfy noUnusedParameters 2026-03-20 20:58:42 +01:00
Emrah Baysal
7ea01bd2de feat(catalyst): add official brand logos for all 50+ platform components
Each component now shows its official brand color + recognisable inline SVG
mark (18×18) in the component selection list. Logos dim when deselected and
reach full opacity when selected, giving clear visual feedback.

Covers all 9 blocks: PILOT, SPINE, SURGE, SILO, GUARDIAN, INSIGHTS,
FABRIC, CORTEX, RELAY — all 52 components.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:57:19 +01:00
Emrah Baysal
39181d7565 feat(catalyst): provider step — custom dropdown with logos, always auto-select region
Custom dropdown component with provider logos, chevron animation, dark
panel with check indicator. Region auto-selected on provider change
(HQ-suggested or first available). Fallback to first provider+region
when no HQ hint matches.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:50:09 +01:00
Emrah Baysal
30fa2cfb68 feat(catalyst): provider step — dropdown UX, compact cards side by side 2026-03-20 20:38:45 +01:00
Emrah Baysal
294bf3ffe1 feat(catalyst): provider step — always-open cards, HQ-based defaults, radio UX
- All region cards always fully expanded side-by-side (no accordion)
- Grid layout: 1/2/3 columns matching topology region count
- Radio button semantics: dot indicator for provider + cloud region selection
- HQ-based pre-selection: parses orgHeadquarters to suggest nearest provider
  and stagger cloud regions across topology regions for built-in redundancy
  (e.g. Frankfurt → Hetzner FSN1/NBG1/HEL1 across 3 regions)
- '★ nearest' badge on recommended provider when not yet configured
- HQ hint banner at top shows which location drove the suggestions
- Credentials summary shown once all regions are fully configured

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:21:48 +01:00
Emrah Baysal
35ce0c9cba fix(catalyst): stalwart tier mandatory→recommended — deselectable in optional RELAY block 2026-03-20 19:08:56 +01:00
Emrah Baysal
18d9498b64 feat(catalyst): profile-based component recommendations + card header redesign
- Recommended items now pre-selected by default in all blocks; optional items off
- Profile-based defaults: industry/compliance/size adjusts which optional blocks
  (FABRIC, CORTEX) are pre-enabled and which recommended components are selected
  e.g. Financial Services → FABRIC(cnpg/valkey/strimzi/debezium) + openmeter + strongSwan
  e.g. Tech/Large orgs → CORTEX(kserve/knative/axon) enabled
- Re-applies automatically when org profile fields change
- Card header: PRODUCT NAME — Subtitle on line 1, 1-line description on line 2
- Added componentsAppliedForProfile to store to avoid overwriting user customizations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 18:39:20 +01:00
Emrah Baysal
bbd256ca23 feat(catalyst): rework component card header — tier chips, grey locked, equal heights
- Remove Required/Optional tag badge from card header
- Subtitle now occupies that space (conceptual name, truncated 1 line = equal heights)
- Tier chips (Xm/Yr/Zo) replace selection count text, color-coded:
  M = always grey/muted (user cannot change mandatory), R = blue, O = indigo
  Chips light up only when items are actually selected
- Locked mandatory items: grey checked checkbox (opacity 0.6) instead of green
  so user clearly understands these cannot be toggled
- Lock icon changed from green to muted white
- Remove SelectionDot (replaced by tier chips as selection indicator)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 18:29:58 +01:00