The last old-look pages. Base app-* classes in _applications.scss now
carry the new spec directly (gradient detail header + accent wash, mono
breadcrumbs/eyebrow panel headers, sk-info-row k/v grids, accent tab
strip) - property-aligned with the .wp-detail-page overrides so WP detail
stays identical and WordPressProject inherits the new chrome for free.
Applications list: PageTopbar + MetricCard KPI strip + dense sk-dtable
(type tiles, Pills, Gauge resource rows, SegControl filter); page no
longer borrows docker-* classes. ApplicationDetail: tinted header tile +
Pill/EnvTag, deploy/build Badges -> Pills, consoles on --bg-code; dead
blocks with zero consumers dropped. Also fixes pre-existing lint errors
(empty catches, unescaped entities). NOTE: /apps currently redirects to
/services so the list page is orphaned - restyled for consistency.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Log Files + Journal tabs -> single bordered console frame (con-rail
target list with mono eyebrow groups, accent-bg active rows, session-head
viewer bar, log well on --bg-code); Processes tab -> same frame with
sk-dtable-spec sortable table + token gauges; Services tab cards re-
tinted with status stripes. Terminal gets its OWN proc-chip filter chips
(no longer depends on _templates.scss .filter-chip); process/service
statuses -> ds Pills; hashColor/priority palettes retinted to the new
palette. Legacy block kept for shared consumers (JournalControls/
ProcessTable/ServiceCard).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SCSS-only reskin of the FileManager page to the demo's `.fmgr` look:
- page goes full-bleed flush (no floating cards): toolbar / tree rail /
listing / status bar joined by 1px soft borders
- left rail: flat groups under mono-uppercase eyebrow headers, demo tree
rows (accent-bg active, amber folders), dense type filters + disk gauges
- list view: sticky mono-uppercase headers, tighter rows, mono size/
modified/permissions cells
- grid view: transparent cards with surface-2 hover, accent-bg selection
(type-tinted thumbs kept)
- status bar + breadcrumbs go mono; bulk bar becomes a flush accent strip
- type palette literals aligned to the redesign tokens (amber/violet/
green/cyan); context menu + preview drawer re-tinted to surface tokens
- dropped dead blocks from the pre-topbar era (.page-header, .sort-popover,
.filter-chips-row, .quick-access-*, .analysis-*, .toolbar-select)
All capabilities kept (uploads, bulk ops, type filters, disk mounts,
remote targets, shortcuts) — zero JSX changes. Build verified.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Finish the Domains trio visually:
- DNS Zones: records table -> dense .sk-dtable; record type rendered as a colored .dns-rtype chip (A/CNAME/MX/TXT/NS/SOA…); content cell uses .sk-cell-mono. Drop unused Badge import.
- SSL Certificates: per-cert status Badge -> Pill (green Valid / amber Expiring). Drop unused Badge import.
Both already themed from Phase 0; these add the redesign's chip/pill language.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Convert the .wp-site-card grid into a dense .sk-dtable: Site tile + name/port, Environments, Version (mono), Status (Pill green/gray), tag chips, and Open-Site/WP-Admin links. Add a status SegControl (All/Running/Stopped) alongside the existing tag filter; row click still opens the site detail. Omitted SSL/plugins/visits/uptime/server columns (not in the get_sites payload — no list backend) per restyle-first. Drop an unused Badge import.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Visual restyle of the existing .servers-table (no column/capability changes — bulk-select, group, telemetry, and row actions all kept): mono uppercase headers, and thin token-colored telemetry gauges (CPU=accent-bright, RAM=cyan, Disk=green). Region/Load columns omitted (mock-only, no backend field) per restyle-first.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final sidebar sub-menu moved to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared WORDPRESS_TABS (WordPress / Pipeline); render PageTopbar on WordPress (sites list) and WordPressProjects (pipeline).
- Remove the wordpress subItems from the sidebar — NO sidebar item has a sub-menu anymore; the rail is fully flat, matching the demo.
- WordPress list: both headers (loading + main) swapped; Import Site + Create Site actions kept.
- Pipeline list header swapped (title 'Pipeline').
- The Pipeline tab is now always visible (was wpInstalled-gated in the sidebar).
Six infra groups total now use the top-bar layout: Domains, Servers, Services, Files, Monitoring, Marketplace, WordPress. Body restyles still pending per group.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final two sidebar sub-menus moved to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Monitoring group: shared MONITOR_TABS (Monitoring/Status Pages); PageTopbar on both pages (Refresh + Start/Stop + Create-Page actions kept).
- Marketplace group: shared MARKET_TABS (Marketplace/Downloads); PageTopbar replaces the marketplace hero; Downloads header swapped (Import ZIP + Refresh kept).
- Remove the monitoring + marketplace subItems from the sidebar.
Only WordPress (Pipeline) still has a sidebar sub-menu; everything else is now flat + top-bar. Body restyles still pending per group.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move Files + FTP to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared FILE_TABS (Files / FTP Server); render PageTopbar on both pages.
- Remove the files subItems from the sidebar (route /ftp unchanged, reachable from the tab).
- FileManager: top bar added as the first child of the full-bleed fullscreen browser (already a flex column; body keeps filling) — the toolbar, tree, preview, upload tray and all file ops are untouched.
- FTPServer: .page-header swapped for PageTopbar; the conditional install/test/start/stop/restart actions moved verbatim into the top bar.
- Fix a pre-existing unescaped-apostrophe lint error in FTPServer.
Follow-up: restyle the FileManager + FTP bodies.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restyle .new-service-page__mode-card into the demo's centered method-card look (icon chip on top, title, description, corner check on the active card) and tokenize the old teal/indigo active-state literals. GitHub-OAuth repo picker, manifest detection, and the template flow are all preserved. Follow-up: template-catalog grid + Docker-Image/From-Scratch methods.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move Services + its sub-pages to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared SERVICE_TABS (Services/New Service/Templates/Deploy Activity); render PageTopbar on all 4 group pages so the tab group persists.
- Remove the services subItems from the sidebar (routes /services/new, /templates, /deployments unchanged, reachable from the tabs).
- Services: header -> PageTopbar (meta shows total/running; New Service action kept).
- NewService: drop the now-redundant breadcrumb; Connections action kept.
- Templates: search box moved into the top-bar actions.
- Deployments: New Service + Live/Paused + Refresh actions kept.
Follow-up: restyle the bodies (NewService method-cards + template catalog, Services dense table, ServiceDetail tabs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move Servers + its 4 sub-pages to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared SERVER_TABS (Servers/Agent Fleet/Fleet Monitor/Cloud Servers/Config Templates) and render PageTopbar on all 5 pages so the tab group persists.
- Remove the servers subItems from the sidebar (routes unchanged, reachable from the tabs).
- Main Servers page: top bar added ABOVE the full-bleed ops workspace (container -> column); the fleet-health rail, status/group filtering, workbar, bulk actions and pairing are fully preserved — no capability removed.
- Sub-pages (AgentFleet/FleetMonitor/CloudProvision/ServerTemplates): .page-header swapped for PageTopbar, action buttons preserved.
- Fix a pre-existing unescaped-quote lint error in FleetMonitor.
Follow-up: restyle the Servers list body to the demo's dense table + ServerDetail tabs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finish the domain page group so the Domains/DNS Zones/SSL tabs persist on all three:
- Extract DOMAIN_TABS to components/domains/domainTabs.jsx; Domains imports it.
- DNSZones + SSLCertificates pages now render the shared <PageTopbar tabs={DOMAIN_TABS}> in place of their old headers (actions preserved).
- Fix a pre-existing unescaped-apostrophe lint error in SSLCertificates.
DNS/SSL page bodies (lists/forms) are not yet restyled — headers only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First infra page moved to the demo structure (REDESIGN_MAP §6 decision 3):
- Add shared PageTopbar ds primitive (icon + title + routed sub-nav + actions).
- Domains: top bar with Domains/DNS Zones/SSL tabs (replaces the removed sidebar sub-menu), KPI strip (MetricCard), segmented filter (SegControl), .sk-dtable list, and a detail Drawer with SSL/linked-site/status specs + all actions (visit/verify/SSL/delete). Add-domain + enable-SSL modals preserved.
- Remove the domains subItems from sidebarItems.js (/dns, /ssl routes unchanged, reachable from the top-bar tabs).
- Omit registrar/DNSSEC/nameservers columns (no WHOIS backend yet) per restyle-first.
Follow-up: give DNSZones + SSLCertificates pages the same top bar so the tab group persists.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace accidental old-palette literals (old indigo/green/amber/zinc surfaces+borders, white/black washes) with design tokens so they theme correctly in light mode and track a custom accent. Intentional literals (brand colors, categorical log/node/file-type palettes, theme-preview mockups, white-on-color, scrims) were preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 0 of the ServerKit UI redesign (see docs/REDESIGN_MAP.md):
- Remap design tokens to the new infra-console palette behind existing names; add prototype token names (surface ramp, cyan/violet, accent-bright/-bg). Dark/light/system.
- Self-host IBM Plex (woff2 vendored via scripts/fetch-fonts.mjs); no Google Fonts CDN.
- Runtime-derive the full accent ramp in ThemeContext.
- Add shared design-system primitives in components/ds (Pill, EnvTag, SegControl, MetricCard, Gauge, ServiceTile, Sparkline, AreaChart, ScoreGauge, Feed, Drawer).
- Restyle the sidebar (gradient rail, mono section headers, left accent bar) and switch the brand to the demo's gradient-tile logo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expose and surface containerised databases and introduce managed app base port control. Backend: add endpoint to list databases across all Docker apps, robustly resolve compose container names, include docker DB user when querying, compute migration needs from live heads and self-heal orphaned alembic_version entries by stamping to head, and add the managed_app_base_port system setting. Template service now honours the managed_app_base_port and WordPress template default HTTP port updated to 8300. Frontend: add react-icons brand icons and DatabaseBrands component, show Docker-hosted DBs under their engine nodes with a docker badge, platform-aware console shortcut label, pass DB user to docker table/query API calls, add API client header merge fix, UI for managing base port in settings, enhanced migration history UI (backup/apply/resync flows, confirm dialog, toasts), and various style tweaks and color additions.
Completes the chosen access-level ACL design: a grant now lets the user actually
operate the resource, gated by a real role.
- Grants carry a role: editor (view + operate) or viewer (read-only). grant POST
validates role in {viewer, editor} (default editor); ResourceGrantService gains
grant_role(); the Sharing UI gains a role selector.
- Two access helpers split the app endpoints: _can_access_app (owner/admin/ANY
grant) gates reads (get_app, /linked, /logs, /container-logs, /containers,
/status); _can_edit_app (owner/admin/EDITOR grant) gates mutations (/link,
/unlink, /environment, PUT, /start, /stop, /restart).
- DELETE stays owner-or-admin (an editor can't delete — tested).
- The same sweep fixed a latent int-vs-str ownership bug across ~14 app endpoints
(compared app.user_id to the stringified get_jwt_identity(); now user.id).
Verified: 3 more ACL tests (viewer read-only + can't operate, editor operates +
can't delete, role validation/default), full suite 130 passed, lint + build.
Still owner-only: env-vars/databases/domains blueprints (follow-up sweep).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the per-site ACL feature with UI on top of the backend (d9a5ec7).
- A per-app 'Share' action in the Workspaces Resources modal opens an inline
sub-view: current grantees with Revoke, plus a grant-access list of users
(the owner and existing grantees are filtered out).
- Client methods getAppGrants / grantAppAccess / revokeAppAccess.
Verified: frontend lint-clean (no new warnings) + production build. The grant
endpoints are covered by backend tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the reassignment feature symmetrically — the server reassign endpoint
shipped earlier (e89415f) was API-only; now it has UI.
- getServers gains an allWorkspaces bypass (empty X-Workspace-Id header) and a
setServerWorkspace client method (mirrors apps).
- The per-workspace modal is now a 'Resources' modal with both Applications and
Servers sections, each with move-in / move-out.
Verified: frontend lint-clean (no new warnings) + production build. The server
endpoint itself is already covered by backend tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the organizing loop — existing resources were stuck wherever the
backfill put them (Default); now they can be moved into client workspaces.
- PUT /apps/<id>/workspace (owner-or-admin on the app) and
PUT /servers/<id>/workspace (developer+), both requiring the TARGET be a
workspace the caller can access (member or admin). A null target moves the
resource back to Default.
- UI: an 'Apps' management modal on each Workspaces card (mirrors the Members
modal) with move-in / move-out. It fetches with allWorkspaces (an empty
X-Workspace-Id header) so it can see apps in other workspaces to move them in,
sidestepping the ambient scope.
- Fixed a real bug caught by tests: the app ownership check compared app.user_id
to the stringified get_jwt_identity() (int != str -> spurious 403); now uses
user.id.
Verified: 2 new endpoint tests (ownership + target-membership + null->Default +
role gates), full suite 122 passed, frontend lint-clean + production build.
Servers reassignment is API-only for now; the modal is apps-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Selecting a client workspace now recolors the whole panel to that client's brand
— the visible payoff of the workspace effort.
- A workspace's primary_color (already on the model) is now editable via a color
picker added to the Workspaces create modal.
- WorkspaceSwitcher mirrors the active workspace's color into a workspace_accent
localStorage key; ThemeContext applies it via the existing applyAccentToDOM with
precedence over the user's personal accent (workspace_accent || accentColor),
recoloring the logo gradient, buttons, and highlights.
- Deterministic (no effect-ordering races): switching reloads and the value is
read once on mount. Cleared on logout and on a stale selection; reverts to the
user accent on 'All workspaces'. Default color is the app accent, so a workspace
is unbranded unless a custom color is chosen.
Verified: frontend lint-clean (no new warnings) + production build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Makes the workspace scoping foundation user-visible — the research flagged the missing 'current workspace' context as the core gap #33 had to close.
- WorkspaceSwitcher (sidebar): self-contained selector that reads/writes an
active_workspace_id in localStorage, fetched from the user's workspaces. Hides
for single-workspace installs and drops a stale selection on load.
- services/api/client.js: sends the active workspace ambiently as X-Workspace-Id
on every request (endpoints that don't honor it ignore it); cleared on logout.
- Switching reloads so all lists re-fetch under the new scope.
Backend resolve_workspace_id is lenient (prior commit) so a stale header can
never break the apps/servers lists. Verified: frontend lint-clean + production
build; the header->scoping path is covered by the backend API tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The self-contained reports slice of the #33 agency-scale milestone, built
without the high-blast-radius workspace_id/ACL/branding rework.
- WpReportsService aggregates one calendar month per site: uptime % + a daily
series + incidents (re-aggregated from #26 HealthCheck samples for the exact
month), update runs (#29), DatabaseSnapshot backups, and current vulnerability
posture (#28) + health/disk — each labelled persisted-history vs point-in-time.
- Persisted (WordPressReport, Alembic 014 off 013_ai_assistant) + synchronous
upsert per (site, YYYY-MM), so point-in-time sources (vulns, health) are
captured as-of generation while true-historical sources are re-queried per
month. Client grouping derives from the client-* tag (#20) — no workspace_id
FK. Model lives in wordpress_site.py + lazy-imported in the service so the
concurrent AI agent's models/__init__.py is untouched.
- Routes GET /reports, POST /reports/generate (admin), DELETE /reports/<id>;
client getReports/generateReport/deleteReport; a Reports tab with a recharts
daily-uptime chart, summary cards, Print->Save-as-PDF (scoped @media print,
no PDF dep) and JSON export.
- 9 pytest tests; 3-lens adversarial review (4 of 5 findings fixed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reworks Git, Services, Firewall, FTP, Deployments, Monitoring, and
Marketplace pages onto the shared StatCard/card system and removes their
now-redundant page-specific SCSS (net large reduction in duplicated
styles).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Moves Security page and its Overview tab onto the shared StatCard/card
styles and trims the now-redundant per-page SCSS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Grows the shared card stylesheet and StatCard component so page-level
surfaces can drop their bespoke card SCSS and reuse one vocabulary.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the five-tab Databases page with a DataGrip-style, full-bleed
Database Explorer built on the file-manager shell:
- unified source tree for all engines with inline + right-click actions
(open console, back up, drop, query, copy name)
- tabbed workspace: click a table to open a paginated data-grid tab,
open SQL consoles per database, and a Backups tab
- toolbar New menu (console / create database / create user, disabled
when the engine is not installed) and a live status bar
- frontend-only: table browsing runs SELECT ... LIMIT/OFFSET via the
existing query endpoints; no API changes
Rewrites _databases.scss into the explorer design system (tree, tabs,
results grid, console, context menu, status bar) using existing tokens;
respects prefers-reduced-motion.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AI is a core ServerKit primitive (not a plugin): an Intercom-style bubble + slide-in drawer on every dashboard page, with a tools+page-context assistant mode and a plain simple chat mode, SSE streaming, conversation persistence, and guarded write actions (the model proposes; the user approves).
Plugins EXTEND the assistant rather than rebuilding it: backend via app.plugins_sdk.ai (reg.tool + register_context_provider, discovered from a plugin.json ai block with install/enable/disable hooks); frontend via useServerkitAI() and a contributions.ai category (suggested_prompts, tool_renderers). The marketplace gains an ai category for discovery.
Backend: ai_service (Prompture Conversation + ProviderEnvironment + guardrails), ai_tool_registry singleton, ai_tools_builtin (core.* tools), the /api/v1/ai blueprint (SSE via a worker-thread + queue bridge, single-gevent-worker safe; in-thread confirmation gate), AiConversation/AiMessage/AiPendingAction models + migration 013, encrypted ai_* settings. Frontend: AIContext + components/ai/*, AI settings tab. Powered by Prompture (installed editable; requirements pinned).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Background safe update (Docker-correct, via wp_cli): record pre-update versions
-> wp db export a DB snapshot (600s timeout via a new wp_cli timeout param so a
large DB is never truncated; ABORTS if the snapshot fails) -> apply updates only
to components with an available update minus an exclusion list -> a quiet,
side-effect-free health probe (wp eval + HTTP probe; does NOT fire #27 alerts)
-> if the update regressed a previously-healthy site, AUTO-ROLLBACK: version-pin
each updated component back (--force --skip-plugins --skip-themes, so it runs
even on a fatally-broken site) + re-import the snapshot -> re-check -> persist a
WordPressUpdateRun report. The snapshot is kept for manual restore whenever the
site does not end verified-healthy.
A per-site cron schedule + exclusion list is driven by a new daemon-thread
scheduler bounded by a concurrency semaphore (a shared 3am cron can't stampede
the host). New model WordPressUpdateRun + auto_update_schedule/auto_update_exclude
columns (Alembic 012). New Updates tab (run-now, schedule, exclusions, history).
Also fixes a latent #26/#27 bug: EnvironmentHealthService.check_health returned
'unknown' for every production site (_get_compose_path needs a container_prefix
they lack), making uptime/incidents/alerts inert for the primary site type — now
falls back to the Application root_path compose.
Deferred: staging-first promotion (needs a staging env + env-pipeline
orchestration). Verified: backend py_compile, frontend lint-clean (full build was
blocked only by a concurrent agent's in-progress AI import, not #29 code),
2-lens adversarial review (blockers fixed: production health gating, DB-import
timeout; majors fixed: scheduler stampede, snapshot-abort).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A per-site Security tab (Docker-correct, all via the WP-CLI bridge):
- File integrity: a background wp core verify-checksums + wp plugin
verify-checksums --all (in-container, reaches the volume; off the request
thread, UI polls) flagging tampered/unexpected files.
- Debug toggle: WP_DEBUG/SCRIPT_DEBUG with WP_DEBUG_DISPLAY=false and
WP_DEBUG_LOG=/tmp/wp-debug.log — OUTSIDE the web root, so the log is never
publicly fetchable (the default wp-content/debug.log is apache-served, a real
info leak this avoids). The write is gated so a read-only/stopped site reports
an honest failure.
- WP-Cron: DISABLE_WP_CRON status + due-event list, run-due-now, enable/disable
(with a clear "needs a real system cron" warning).
Routes mirror the per-site pattern (owner-or-admin GET, @admin_required
mutations). Deferred: the brute-force wp-login/xmlrpc rate-limit jail —
Fail2ban/nginx limit_req both need a host-side per-site access log the
localhost:PORT apache-container model does not expose (same gap as #21/#25).
Verified: backend py_compile, frontend lint + build, focused security review
(debug-log web-exposure caught + fixed). Not runtime-exercised on a real host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cross-reference a site's installed plugin/theme/core slug+version against the
keyless community WPVulnerability API and surface findings per site.
WpVulnerabilityService reads installed versions via the Docker-aware WP-CLI
bridge (wp core version + get_plugins/get_themes directly — not the
host-filesystem-gated get_wordpress_info, which fails for volume-backed Docker
sites), queries https://www.wpvulnerability.net/{plugin,theme,core}/ (no key,
1h in-memory cache, descriptive UA, 8s timeout), matches the installed version
against each advisory operator bounds (core advisories all apply — the feed is
version-scoped), and extracts CVE id / severity (cvss3 -> cvss short-code ->
score) / fixed-in / reference.
Persisted as a new WordPressVulnerability child model (FK -> wordpress_sites.id)
+ a last_vuln_scan_at column (Alembic 011, idempotent). The scan runs in a
background thread (Lynis-style) so the single worker never blocks; a new
Vulnerabilities tab polls for status and renders findings with severity badges,
CVE links, and a summary. On-demand only.
Security (3-lens review): reference_url restricted to http(s) (blocks
javascript: hrefs); feed slug percent-encoded; unrecognized version operator
skips rather than over-flags; the major Docker volume-backed core-read gating
was fixed so normal sites scan. Verified: backend py_compile, frontend lint +
build. Not runtime-exercised on a real host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PromoteModal already had files-only (code), db-only (database), and
specific-folders (include_plugins/themes/mu_plugins/uploads). The only missing
selective-push control was specific-tables, now added to the database section
as two comma-separated inputs:
- Exclude tables — omitted from the promotion entirely
- Truncate tables — structure promoted, rows dropped
Both split into arrays on submit. The backend already honored them end-to-end
(POST .../promote forwards config.exclude_tables/truncate_tables ->
promote_database/promote_full -> clone_options -> _transform_dump), so this is
pure UI wiring, no backend change. Verified: frontend lint + build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reworks the Settings > Activity tab away from stat-card tiles and icon'd
log entries toward a tighter, terminal-adjacent readout:
- Activity summary becomes a single inline stats line (active today /
actions this week / total users) instead of three stat cards.
- Audit log entries render as compact log rows (role="list") with inline
key=value details, dropping the per-row action icon and detail cards.
- _users.scss sheds the old card/icon styling for the leaner row layout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>