On long deployment histories the StartupLogPanel would render off-screen
when the user clicked a row. Ref + useEffect scrolls the panel into view
with block:'nearest' so expanding a row that's already in view doesn't
cause a disorienting jump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleRestore previously returned silently when deployedConfigSnapshot
was null, leaving the user wondering why their click did nothing. Show
a warning toast explaining that the checkpoint predates snapshotting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Snapshot is written by DeploymentExecutor before the RUNNING/DEGRADED
split, so DEGRADED rows already carry a deployed_config_snapshot. Treat
them as checkpoints — partial-healthy deploys still produced a working
config worth restoring. Aligns repo query with UI filter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bug 1: default serverDirtyAgainstDeploy to true (not false) while
dirtyState query is loading — prevents the button showing 'Save'
instead of 'Redeploy' on apps with no successful deployment yet.
- Bug 2: normalize samplingRate from server as '<n>.0' when the value
is a whole-number float so serverState matches form after save,
eliminating spurious dirty detection that kept Save enabled.
- Bug 3: add success toast after handleSave completes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Issue 1: add List<String> sensitiveKeys as 4th field to DeploymentConfigSnapshot; populate
from agentConfig.getSensitiveKeys() in DeploymentExecutor; handleRestore hydrates from
snap.sensitiveKeys directly; Deployment type in apps.ts gains sensitiveKeys field
- Issue 2: after createApp succeeds, refetchQueries(['apps', envSlug]) before navigate so the
new app is in cache before the router renders the deployed view (eliminates transient Save-
disabled flash)
- Issue 3: useDeploymentPageState useEffect now uses prevServerStateRef to detect local edits;
background refetches only overwrite form when no local changes are present
- Issue 5: handleRedeploy invalidates dirty-state + versions queries after createDeployment
resolves; handleSave invalidates dirty-state after staged save
- Issue 10: DirtyStateCalculator strips volatile agentConfig keys (version, updatedAt, updatedBy,
environment, application) before JSON comparison via scrubAgentConfig(); adds
versionBumpDoesNotMarkDirty test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AppsTab.tsx shrunk from 1387 to 109 lines — router now owns /apps/new
and /apps/:slug via AppDeploymentPage; list-only file retained.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add deployedConfigSnapshot field to Deployment interface (mirrors server shape)
- Remove the Task 10.3 cast in handleRestore now that the type has the field
- New useUnsavedChangesBlocker hook (react-router useBlocker, v7.13.1)
- Wire AlertDialog into AppDeploymentPage for in-app navigation guard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DeploymentTab composes StatusCard, DeploymentProgress, StartupLogPanel,
and HistoryDisclosure for the latest deployment. StartupLogPanel gains an
optional className prop, drops the fixed maxHeight, and its .panel rule
uses flex-column + min-height:0 so a parent can drive its height.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collapsible deployment history table (sorted newest-first) with
click-to-expand StartupLogPanel for any historical deployment row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Status badge, replica count, URL, JAR/checksum grid, and stop/start
actions for the latest deployment. CSS added to AppDeploymentPage.module.css.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ports the ConfigSubTab traces/taps and route recording content into
standalone tab components. Each write goes straight to live agents via
useUpdateApplicationConfig (apply='live'). A local draft state prevents
stale reads during the async flush. LiveBanner is rendered at the top of
both tabs to communicate the live-apply semantics.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a warning banner that communicates live-apply semantics (changes
bypass the Save/Redeploy cycle). Uses --warning-bg / --warning-border
DS tokens. CSS class .liveBanner added to AppDeploymentPage.module.css.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure presentational tab receiving SensitiveKeysFormState via value/onChange.
Calls useSensitiveKeys() internally to show global baseline (readonly).
Local useState for the new-key input buffer. Reuses skStyles from
SensitiveKeysPage.module.css for consistent pill/badge layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure presentational tab receiving VariablesFormState via value/onChange.
Rows use the new .envVarsList / .envVarRow CSS grid (1fr 2fr auto).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure presentational tab receiving ResourcesFormState via value/onChange.
Local useState buffers for newPort/newNetwork keep the "add next item"
inputs isolated from form state. isProd prop gates the memory-reserve field.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure presentational tab receiving MonitoringFormState via value/onChange.
Also adds shared config-tab styles to AppDeploymentPage.module.css
(configInline, toggleEnabled/Disabled, portPills, inputSizes, envVarsList/Row).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
P3B of the taxonomy migration. App and deployment routes are now
env-scoped in the URL itself, making the (env, app_slug) uniqueness
key explicit. Previously /api/v1/apps/{appSlug} was ambiguous: with
the same app deployed to multiple environments (dev/staging/prod),
the handler called AppService.getBySlug(slug) which returns the
first row matching slug regardless of env.
Server:
- AppController: @RequestMapping("/api/v1/environments/{envSlug}/
apps"). Every handler now calls
appService.getByEnvironmentAndSlug(env.id(), appSlug) — 404 if the
app doesn't exist in *this* env. CreateAppRequest body drops
environmentId (it's in the path).
- DeploymentController: @RequestMapping("/api/v1/environments/
{envSlug}/apps/{appSlug}/deployments"). DeployRequest body drops
environmentId. PromoteRequest body switches from
targetEnvironmentId (UUID) to targetEnvironment (slug);
promote handler resolves the target env by slug and looks up the
app with the same slug in the target env (fails with 404 if the
target app doesn't exist yet — apps must exist in both source
and target before promote).
- AppService: added getByEnvironmentAndSlug helper; createApp now
validates slug against ^[a-z0-9][a-z0-9-]{0,63}$ (400 on
invalid).
SPA:
- queries/admin/apps.ts: rewritten. Hooks take envSlug where
env-scoped. Removed useAllApps (no flat endpoint). Renamed path
param naming: appId → appSlug throughout. Added
usePromoteDeployment. Query keys include envSlug so cache is
env-scoped.
- AppsTab.tsx: call sites updated. When no environment is selected,
the managed-app list is empty — cross-env discovery lives in the
Runtime tab (catalog). handleDeploy/handleStop/etc. pass envSlug
to the new hook signatures.
BREAKING CHANGE: /api/v1/apps/** paths removed. Clients must use
/api/v1/environments/{envSlug}/apps/{appSlug}/**. Request bodies
for POST /apps and POST /apps/{slug}/deployments no longer accept
environmentId (use the URL path instead). Promote body uses slug
not UUID.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BREAKING: wipe dev PostgreSQL before deploying — V1 checksum changes.
Agents must now send environmentId on registration (400 if missing).
Two tables previously keyed on app name alone caused cross-environment
data bleed: writing config for (app=X, env=dev) would overwrite the row
used by (app=X, env=prod) agents, and agent startup fetches ignored env
entirely.
- V1 schema: application_config and app_settings are now PK (app, env).
- Repositories: env-keyed finders/saves; env is the authoritative column,
stamped on the stored JSON so the row agrees with itself.
- ApplicationConfigController.getConfig is dual-mode — AGENT role uses
JWT env claim (agents cannot spoof env); non-agent callers provide env
via ?environment= query param.
- AppSettingsController endpoints now require ?environment=.
- SensitiveKeysAdminController fan-out iterates (app, env) slices so each
env gets its own merged keys.
- DiagramController ingestion stamps env on TaggedDiagram; ClickHouse
route_diagrams INSERT + findProcessorRouteMapping are env-scoped.
- AgentRegistrationController: environmentId is required on register;
removed all "default" fallbacks from register/refresh/heartbeat auto-heal.
- UI hooks (useApplicationConfig, useProcessorRouteMapping, useAppSettings,
useAllAppSettings, useUpdateAppSettings) take env, wired to
useEnvironmentStore at all call sites.
- New ConfigEnvIsolationIT covers env-isolation for both repositories.
Plan in docs/superpowers/plans/2026-04-16-environment-scoping.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
canSubmit no longer requires a JAR file when "Create only" is selected.
JAR upload and deploy steps are skipped when no file is provided.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously showed an infinite spinner because unmanaged apps have no
PostgreSQL record. Now shows an "Unmanaged Application" message with
a link to create a managed app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apps can now join additional Docker networks (e.g., monitoring,
prometheus) configured via containerConfig.extraNetworks. Flows through
the 3-layer config merge. Networks are created if absent and containers
are connected during deployment. UI adds a pill-list field on the
Resources tab (both create and edit views).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace simple key-value rows with EnvEditor component that supports
editing variables as Table, Properties, YAML, or .env format.
Switching views converts data seamlessly. Includes file import
(drag-and-drop .properties/.yaml/.env) with auto-detect and merge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 7.1: Add deployment status badge (StatusDot + Badge) to AppsTab app
list, sourced from catalog.deployment.status via slug lookup
- 7.3: Add X close button to top-right of exchange detail right panel
in ExchangesPage (position:absolute, triggers handleClearSelection)
- 7.5: PunchcardHeatmap shows "Requires at least 2 days of data"
when timeRangeMs < 2 days; DashboardL1 passes the range down
- 7.6: Command palette exchange results truncate IDs to ...{last8}
matching the exchanges table display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds amber edit-mode banners to AppConfigDetailPage and both
DefaultResourcesSection/JarRetentionSection in EnvironmentsPage,
matching the existing ConfigSubTab pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Override design system tokens in app root CSS: --text-muted raised to 4.5:1
contrast in both light (#766A5E) and dark (#9A9088) modes; --text-faint dark
mode raised from catastrophic 1.4:1 to 3:1 (#6A6058). Migrate --text-faint
usages on readable text (empty states, italic notes, buttons) to --text-muted.
Raise all 10px and 11px font-size declarations to 12px floor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: AgentRegistryService gains findByApplicationAndEnvironment()
and environment-aware addGroupCommandWithReplies() overload.
AgentCommandController and ApplicationConfigController accept optional
environment query parameter. When set, commands only target agents in
that environment. Backward compatible — null means all environments.
Frontend: All command mutations (config update, route control, traced
processors, tap config, route recording) now pass selectedEnv to the
backend via query parameter.
Prevents cross-environment command leakage — e.g., updating config for
prod no longer pushes to dev agents.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes pre-existing TS2322 where Record<string, string> was not
assignable to the StatusDotVariant union type.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate 20+ duplicate function definitions across UI components into
three shared util files (format-utils, agent-utils, config-draft-utils).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Part 1 — Config snapshot:
- V8 migration adds resolved_config JSONB to deployments table
- DeploymentExecutor saves the full resolved config at deploy time
- Deployment record includes resolvedConfig for auditability
Part 2 — Composite health StatusDot:
- CatalogController computes composite health from deployment status +
agent health (green only when RUNNING AND agent live)
- CatalogApp includes healthTooltip (e.g. "Deployment: RUNNING,
Agents: live (1 connected)")
- StatusDot added to app detail header with deployment status Badge
- StatusDot added to deployment table rows
- Sidebar passes composite health + tooltip through to tree nodes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compare app.updatedAt with deployment.deployedAt — if config was
modified after the deployment started, show a primary "Redeploy" button
in the Actions column. Also show a toast hint after saving config:
"Redeploy to apply changes to running deployments."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename cpuShares to cpuRequest (millicores), cpuLimit from cores to
millicores. ResolvedContainerConfig translates to Docker-native units
via dockerCpuShares() and dockerCpuQuota() helpers. Future K8s
orchestrator can pass millicores through directly.
- Fix waitForAnyHealthy to wait for ALL replicas instead of returning
on first healthy one. Prevents false DEGRADED status with 2+ replicas.
- Default app detail to Configuration tab (was Overview)
- Reorder config sub-tabs: Monitoring, Resources, Variables, Traces &
Taps, Route Recording
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>