- Apply cargo fmt to src-tauri/src/commands/kube.rs (CI was failing)
- Update pr-review.yml to use qwen3-coder-next model via liteLLM
- Add TICKET-kube-ui-feature-parity.md gap analysis for FreeLens parity
Co-Authored-By: TFTSR Engineering <noreply@tftsr.com>
Replace hardcoded light-mode Tailwind colors with dark: variants
across six components. Issues that broke readability:
- PiiDiffViewer / Security: toggle knob was bg-white (invisible on
bg-muted in dark mode) -> bg-background
- ImageGallery: thumbnail container, filename labels, alert banners,
and modal chrome all used hardcoded gray/white backgrounds with dark
text; added full dark: variants throughout
- ShellExecution TIER_CONFIG: tier cards used bg-green/yellow/red-50
(near-white) with dark text; added dark:bg-*-950/30 backgrounds and
light text for all three tiers
- ShellApprovalModal: tier 2 badge hardcoded bg-yellow-50/text-yellow-700;
added dark: variants
- LogUpload: PII warning alert used bg-amber-50/text-amber-800; added
dark:bg-amber-900/20 and lighter text for dark mode
Add write_secure_temp_file() helper that creates files with mode 0600
on Unix (owner read/write only) instead of the default 0644
(world-readable). All 41 temp kubeconfig write sites updated.
Kubeconfig files contain cluster credentials; world-readable temp files
would expose them to any local user on the system.
- Add detect_auth_method() to identify kubeconfig credential type
(exec plugin, bearer token, inline cert, file-path cert, basic auth)
and surface warnings when the auth requires an external binary or file
- Split test into Stage 1 (kubectl get --raw=/healthz, no auth) and
Stage 2 (kubectl cluster-info, authenticated), so connectivity and
auth failures are reported distinctly rather than collapsing both
into opaque memcache.go noise
- Output now includes auth method and per-stage result for faster
diagnosis of 'server requires credentials' vs unreachable host
Credential error persists: switch all 40 kubectl invocations from using
KUBECONFIG env var to the explicit --kubeconfig CLI flag. The flag has higher
precedence in kubectl's lookup order and is unambiguous regardless of any
inherited KUBECONFIG env var in the parent process environment.
Also adds test_kubectl_connection Tauri command (runs kubectl cluster-info
with the stored kubeconfig) and a Test button in Settings → Kubeconfig so
the exact kubectl output — context name, exit code, full stdout/stderr — is
visible without needing to inspect tracing logs. This output will reveal
whether the issue is expired certs, a missing exec-auth plugin, wrong context,
or something else entirely.
## kubectl credentials still failing after --context fix
Root cause: both extract_context() (kube.rs) and upload_kubeconfig() (shell.rs)
ignored the kubeconfig's current-context field and always picked contexts[0] from
the contexts array. If a kubeconfig has multiple contexts and current-context
points to entry N>0, we silently used the wrong context — one that may have empty
or expired credentials — causing the 401 "the server has asked for the client to
provide credentials" error on every kubectl call.
Fixes:
- extract_context(): read current-context field first; fall back to contexts[0]
only when current-context is absent or empty.
- extract_current_context_name(): new helper in kubeconfig.rs using the same
line-scanner approach as parse_kubeconfig_contexts (no extra dependencies).
- upload_kubeconfig(): use current-context to select the matching context entry
when storing context name in kubeconfig_files; falls back to first entry.
NOTE: existing kubeconfig rows in the database have the old (wrong) context
stored. Re-uploading kubeconfig files after deploying this build will fix them.
## Cluster dropdown still showing UUID
Root cause: SelectValue rendered ctx.value (the raw UUID passed to SelectItem's
value prop) instead of the display label (SelectItem's children). The custom
Select component had no mechanism to mirror a selected item's children into the
trigger area.
Fix: Select now builds a value→label Map by walking the children tree at render
time (collectLabels). The map is memoised on children. SelectValue reads the
display label from the map; if found, shows the label; otherwise falls back to
the raw value so existing behaviour is preserved for callers that don't need it.
1. kubectl credentials error (41 places in kube.rs)
Every kubectl invocation used .env("KUBERNETES_CONTEXT", context) which
is not a real kubectl environment variable — kubectl silently ignores it
and falls back to whatever current-context is set in the kubeconfig YAML.
If that context has expired or wrong credentials the auth failure occurs.
Replaced all 41 instances with .arg("--context").arg(context) so kubectl
always uses the correct context from the stored kubeconfig.
2. Cluster name still showed UUID (two causes)
a) Hotbar read from kubernetesStore.clusters (ClusterInfo[]) which is never
populated by the kubeconfig-based flow — always empty, so selectedCluster
was always undefined. Removed the Zustand cluster lookup from Hotbar and
added a clusterName prop passed from KubernetesPage.tsx (selectedConfig?.name).
b) ClusterOverview fell back to showing raw clusterId UUID when clusterName
was undefined. Changed subtitle to render conditionally so UUID never shows.
3. Bell dialog had no way to close
Custom DialogContent had no X button and no backdrop-click handler.
Added X close button (top-right) and backdrop-click-to-close.
4. Hotbar icons invisible in dark mode
variant="ghost" only styles hover state with no baseline text color.
Added className="text-foreground" to all icon-only ghost buttons.
Bug 1 — Dead multi-word tier3 entries / missing single-token commands
parse_single_command() extracts only the first token as `command`, so
multi-word entries like "kill -9", "init 0", "service stop" in the tier3
array never matched. Adding the single-token forms "kill", "pkill",
"killall", "init" to TIER3_COMMANDS ensures these commands are always
denied. Removed all dead multi-word entries.
Bug 2 — systemctl Tier 1 special case was dead code
systemctl was not in tier1_general, so the block that was supposed to
auto-execute `systemctl status` never ran. Moved systemctl handling into
its own block (TIER1_SYSTEMCTL_SUBCOMMANDS / TIER2_SYSTEMCTL_SUBCOMMANDS)
evaluated before the general tier checks. status, is-active, is-enabled,
list-units, list-unit-files → Tier 1; all others → Tier 2.
Bug 3 — ldapmodify / ldapdelete / ldapadd misclassified as Tier 1
Both appeared in the old tier1_general and tier2_general arrays; the tier1
check ran first, so LDAP write operations auto-executed. Removed them from
tier1. ldapsearch (read-only) remains Tier 1.
Dynamic Safety Architecture UI
Extracted all tier classification arrays to module-level pub const slices
(TIER3_COMMANDS, TIER1_KUBECTL_SUBCOMMANDS, etc.) so both the classifier
logic and a new get_classifier_rules() Tauri command share a single source
of truth. ShellExecution.tsx now calls getClassifierRulesCmd() on mount and
renders the actual command lists in collapsible per-tier cards — any change
to the const arrays is automatically reflected in the UI with no manual
documentation update needed.
Also fixes the cargo fmt CI failure introduced in the previous commit
(ClusterClient::new call reformatted to a single line).
Resolves four bugs in the Kubernetes management interface:
1. **Cluster not found error** - commands/kube.rs::list_nodes (and all other
kube resource commands) look up clusters from state.clusters (in-memory map)
which was never populated from the kubeconfig_files table. Add a new
connect_cluster_from_kubeconfig Tauri command that reads the encrypted
kubeconfig from the DB, decrypts it, and inserts a ClusterClient into
state.clusters. Wire it into KubernetesPage on initial load and cluster
change so the in-memory map is always populated before any kube command runs.
2. **Dropdown selection has no effect** - same root cause as #1; activating a
kubeconfig only updated the DB flag but never loaded the client into memory.
handleClusterChange now calls connectClusterFromKubeconfigCmd after activation.
3. **GUID shown instead of cluster name** - ClusterOverview displayed the raw
internal UUID as the page subtitle. Now accepts a clusterName prop (populated
from kubeconfig.context) and renders that instead. ClusterDetails similarly
changed to show kubeconfig.context in the header, not the UUID.
4. **Bell icon not clickable** - Hotbar bell button had no onClick handler. Add
optional onNotifications / notificationCount props; badge count is now dynamic
rather than hardcoded. KubernetesPage wires up a notifications dialog showing
active cluster context and a link to the Events section.
All changes follow TDD: failing tests written first, then implementation.
git-cliff's --tag flag sets the display label for unreleased commits;
it does not scope commits to a range. Passing a range string to --tag
caused git-cliff to emit the full cumulative history for every release.
Move the revision range from --tag to a positional argument so only
commits between PREV_TAG and CURRENT_TAG appear in each release body.
CHANGELOG.md generation is unaffected (still full history).
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
- Update handleActivateKubeconfig to call setSelectedCluster after activation
- ResourceBrowser now loads resources for the activated kubeconfig's cluster
- All tests passing, build successful
- Remove duplicate 'Add Cluster' button and modal
- Remove duplicate 'Start Port Forward' button and modal
- KubernetesPage now uses kubeconfig files from Settings → Kubeconfig
- Clusters section displays kubeconfig files with active indicator
- Port forwarding section shows active port forwards without duplicate controls
- All tests passing, build successful
Add a new 'Fetch PR comment history' step that pulls both review posts
and issue comments from the Gitea API before the LLM is called.
The full comment history is injected into the prompt with an explicit
instruction to silently discard any finding already marked as invalid,
acknowledged as intentional, or confirmed fixed in a prior round.
This prevents the reviewer from repeatedly raising refuted findings
across successive push events on the same PR.
- Remove duplicate state.inner() calls in subscribe_to_k8s_events and
subscribe_to_all_k8s_events (copy-paste error)
- Share all AppState Arc fields in OAuth callback task — clusters,
port_forwards, refresh_registry, and watchers were previously
constructed as fresh isolated instances instead of being cloned from
the live AppState
- Replace infinite sleep loop in Watcher::start with an immediate
warn-and-return, preventing Tokio thread leaks from stub watchers