# IntelliJ AI Assistant — Structural Refactor Prompt You are performing a focused structural refactor on the **knoe** installer project. Make **ONLY** the changes described below. Do **NOT** add features, rename identifiers not listed, or change observable behaviour. After each task, run the existing test suite and stop + report if any tests fail. --- ## TASK A — Split `ops/registry.py` into mode-specific modules ### A1. Create `knoe/core/ops/k3d_registry.py` - Move `_ensure_k3d_registry()` from `registry.py` - Implement `initialize`, `start`, `update`, `stop`, `restart`, `status` for **k3d** mode (extract the k3d branches that currently live inside each function in `registry.py`) - `update()` calls `_ensure_k3d_registry()` - `stop()` deletes the k3d registry and the fallback Docker container - `status()` checks `k3d registry list` ### A2. Create `knoe/core/ops/k3s_registry.py` - Move `_manifest()` and `_deployment_is_available()` from `registry.py` - Implement `initialize`, `start`, `update`, `stop`, `restart`, `status` for **k3s** mode (this is the current default fall-through path in `registry.py`) - `update()` applies `k8s/registry/deployment.yaml`, reconciles ReplicaSets, waits rollout ### A3. Create `knoe/core/ops/k8s_registry.py` - `update()` / `stop()` / `restart()`: log a no-op message and return - `status()`: return `True` (GCP Artifact Registry is externally managed) - `initialize()` / `start()`: delegate to `update()` ### A4. Rewrite `knoe/core/ops/registry.py` as a pure dispatcher ```python from ._services_common import _detect_mode from types import ModuleType def _module(mode, env) -> ModuleType: m = _detect_mode(mode, env) if m == "k3d": from . import k3d_registry; return k3d_registry if m == "k8s": from . import k8s_registry; return k8s_registry from . import k3s_registry; return k3s_registry # initialize / start / update / stop / restart / status # — each delegates to _module(mode, env).(**kwargs) # Public signatures are UNCHANGED. ``` --- ## TASK B — Split `ops/garage_store.py` into mode-specific modules ### B1. Create `knoe/core/ops/_garage_common.py` - Move `_garage_namespace()` and `_ensure_garage_secret()` from `garage_store.py` - These helpers are shared by all three mode variants ### B2. Create `knoe/core/ops/k3s_garage_store.py` - Move `_repair_released_garage_pvs()` here (k3s / Synology iSCSI only) - `_manifest_files()` returns: `storageclass-synology-iscsi.yaml`, `iscsi-pvs.yaml`, `garage-configmap.yaml`, `garage-statefulset.yaml`, `garage-service.yaml` - `update()` calls `_repair_released_garage_pvs()` before applying manifests - Cluster-scoped resources (no `-n` flag): `{"storageclass-synology-iscsi.yaml", "iscsi-pvs.yaml"}` ### B3. Create `knoe/core/ops/k3d_garage_store.py` - `_manifest_files()` returns: `garage-configmap.yaml`, `garage-statefulset.yaml`, `garage-service.yaml` (no Synology storage classes, no static PVs) - No PV repair ### B4. Create `knoe/core/ops/k8s_garage_store.py` - `_manifest_files()` returns: `storageclass-gcp-hdd.yaml`, `garage-configmap.yaml`, `garage-statefulset-gcp.yaml`, `garage-service.yaml` - No PV repair - Cluster-scoped: `{"storageclass-gcp-hdd.yaml"}` ### B5. Rewrite `knoe/core/ops/garage_store.py` as dispatcher Same `_module()` + delegating-function pattern as Task A. `stop()` and `restart()` in garage variants take only `namespace`, `env`, `log` (no `mode` parameter — mode is resolved from `env`). --- ## TASK C — Rename UI nav labels (display text only) **File: `knoe/ui/screens/__init__.py`** ``` "Kerberos Authentication" → "Knoe Authority" (nav_items; page_id "kerberos_config" unchanged) "Knoe User Authority" → "Knoe Users" (nav_items; page_id "knoe_users" unchanged) ``` **File: `knoe/ui/screens/security.py`** - Change the `_render_title("Kerberos Authentication", ...)` call → `_render_title("Knoe Authority", ...)` - Change `text="Enable Kerberos Authentication"` → `text="Enable Knoe Authority"` - **Do NOT** change `self.knoe_cfg_data["Kerberos Authentication"]` keys — those are config-file section names **File: `knoe/ui/screens/knoe_users.py`** - Update any user-facing title Label text that still reads "Knoe User Authority" - **Do NOT** change log prefixes or cfg/env key names --- ## TASK D — Add k3d | k3s | k8s mode tab strip to sidebar **File: `knoe/ui/screens/__init__.py`** — in `__init__`, after `self.nav_widgets = {}`: ```python self._mode_tab_widgets: dict = {} self.deployment_mode = tk.StringVar(value=os.environ.get("KNOE_MODE", "k3s")) ``` **File: `knoe/ui/screens/navigation.py`** — in `_create_sidebar_nav()`, **before** the `"INSTALLER"` Label, insert: ```python _MODE_COLORS = {"k3d": "#4A90D9", "k3s": "#27AE60", "k8s": "#E67E22"} mode_frame = tk.Frame(self.sidebar, bg="#F5F5DC") mode_frame.pack(fill="x", padx=12, pady=(14, 4)) for label, value in [("k3d", "k3d"), ("k3s", "k3s"), ("k8s", "k8s")]: btn = tk.Label( mode_frame, text=label, bg="#D5D5C5", fg="#555", font=("SF Pro Text", 9, "bold"), padx=8, pady=3, cursor="hand2", ) btn.pack(side="left", padx=2) btn.bind("", lambda e, v=value: self._set_deployment_mode(v)) self._mode_tab_widgets[value] = btn self._refresh_mode_tabs() ``` Add to the navigation mixin: ```python def _set_deployment_mode(self, mode: str) -> None: self.deployment_mode.set(mode) os.environ["KNOE_MODE"] = mode self._refresh_mode_tabs() def _refresh_mode_tabs(self) -> None: _MODE_COLORS = {"k3d": "#4A90D9", "k3s": "#27AE60", "k8s": "#E67E22"} active = self.deployment_mode.get() for value, widget in self._mode_tab_widgets.items(): if value == active: widget.configure(bg=_MODE_COLORS[value], fg="white") else: widget.configure(bg="#D5D5C5", fg="#555") ``` --- ## CONSTRAINTS - All existing import paths must remain valid (`from knoe.core.ops import registry` etc.) - All public function signatures are unchanged - Run tests after each task; stop and report failures before continuing - Do not create README or documentation files (other than this prompt file) - Do not touch any files not listed above