Problem: After waitForAngular, content assertions ran before Angular's
XHR data loaded. Tests checked textContent('body') at a point when
the table/heading hadn't rendered yet.
Fix: Replace point-in-time checks with Playwright auto-retry assertions:
- expect(locator).toBeVisible({ timeout: 15_000 }) — retries until visible
- expect(locator).toContainText('X', { timeout: 15_000 }) — retries until text appears
- expect(rows.first()).toBeVisible() — retries until table has data
Also: landing page test now uses waitForFunction to detect Angular redirect.
10 files changed, net -45 lines (simpler, more robust assertions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The generic waitForAngular matched the sidebar nav immediately but
route content (tables, tabs, forms) hadn't rendered yet.
Updated waitForAngular selector to wait for route-level elements:
stella-page-tabs, .integration-list, .source-catalog, table tbody tr,
h1, [role=tablist], .detail-grid, .wizard-step, form.
Also fixed activity-timeline and pagination tests (still had
waitForTimeout(2_000) instead of waitForAngular).
Increased fallback timeout from 5s to 8s for slow-loading pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The 3s waitForTimeout after page.goto wasn't enough for Angular to
bootstrap and render content. Replace with waitForAngular() helper
that waits for actual DOM elements (nav, headings) up to 15s, with
5s fallback.
32 calls updated across 10 test files.
Also adds waitForAngular to helpers.ts export.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The advisory source API tests go through the Valkey transport with
withRetry (3 attempts). With the 55s transport timeout, worst case
is 3 × 55s = 165s, exceeding the default 120s test timeout.
Set advisory lifecycle describe block to 300s via beforeEach to
give enough headroom for all retry attempts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes resolving the cascading test failures:
1. Add withRetry() to integrations.e2e.spec.ts advisory section — the
6 API tests that 504'd on Concelier transport now retry up to 2x
2. Change all UI test page.goto from networkidle to domcontentloaded
across 9 test files — networkidle never fires when Angular XHR
calls 504, causing 30 UI tests to timeout. domcontentloaded fires
when HTML is parsed, then 3s wait lets Angular render.
3. Fix test dependencies — vault-consul-secrets detail test now creates
its own integration instead of depending on prior test state.
New test: catalog page aggregation report — verifies the advisory
source catalog page shows stats bar metrics and per-source freshness
data (the UI we built earlier this session).
Files changed: integrations.e2e.spec.ts, vault-consul-secrets, ui-*,
runtime-hosts, gitlab-integration, error-resilience, aaa-advisory-sync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Problem: Each test created a new browser context and performed a full
OIDC login (120 logins in a 40min serial run). By test ~60, Chromium
was bloated and login took 30s+ instead of 3s.
Fix: apiToken and apiRequest are now worker-scoped — login happens
ONCE per Playwright worker, token is reused for all API tests.
liveAuthPage stays test-scoped (UI tests need fresh pages).
Impact: ~120 OIDC logins → 1 per worker. Eliminates auth overhead
as the bottleneck for later tests in the suite.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The POST /sync and POST /{sourceId}/sync tests start background fetch
jobs that degrade the Valkey messaging transport, causing 504 timeouts
on all subsequent Concelier API calls in the test suite.
Gate these two tests behind E2E_ACTIVE_SYNC=1 so the default suite
only runs read-only advisory source operations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Even a single sync trigger starts a background fetch job that degrades
the Valkey messaging transport for subsequent tests. Gate all sync
POST tests behind E2E_ACTIVE_SYNC=1 so the default suite only tests
read-only operations (catalog, status, enable/disable, UI).
Also fix tab switching test to navigate from registries tab (known state)
and verify URL instead of aria-selected attribute.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: The gateway's Valkey transport to Concelier has a ~30s
timeout. Under load, API calls to advisory-sources endpoints return
504 before the Concelier responds. This is not an auth issue — the
auth fixture works fine, but the API call itself gets a 504.
Fix: Add withRetry() helper that retries on 504 (up to 2 retries
with 3s delay). This handles transient gateway timeouts without
masking real errors. Also increased per-test timeout to 180s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three-layer defense against Concelier overload during bulk advisory sync:
Layer 1 — Freshness query cache (30s TTL):
GET /advisory-sources, /advisory-sources/summary, and
/{id}/freshness now cache their results in IMemoryCache for 30s.
Eliminates the expensive 4-table LEFT JOIN with computed freshness
on every call during sync storms.
Layer 2 — Backpressure on sync endpoint (429 + Retry-After):
POST /{sourceId}/sync checks active job count via GetActiveRunsAsync().
When active runs >= MaxConcurrentJobs, returns 429 Too Many Requests
with Retry-After: 30 header. Clients get a clear signal to back off.
Layer 3 — Staged sync-all with inter-batch delay:
POST /sync now triggers sources in batches of MaxConcurrentJobs
(default: 6) with SyncBatchDelaySeconds (default: 5s) between batches.
21 sources → 4 batches over ~15s instead of 21 instant triggers.
Each batch triggers in parallel (Task.WhenAll), then delays.
New config: JobScheduler:SyncBatchDelaySeconds (default: 5)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Completes Sprint 323 TASK-001 using Option C (direct URL rewrite):
- release-management.client.ts: readBaseUrl and legacyBaseUrl now use
/api/v1/release-orchestrator/releases, eliminating the v2 proxy dependency
- All 15+ component files updated: activity, approvals, runs, versions,
bundle-organizer, sidebar queries, topology pages
- Spec files updated to match new URL patterns
- Added /releases/activity and /releases/versions backend route aliases
in ReleaseEndpoints.cs with ListActivity and ListVersions handlers
- Fixed orphaned audit-log-dashboard.component import → audit-log-table
- Both Angular build and JobEngine build pass clean
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: after 20+ minutes of serial test execution, the OIDC login
flow becomes slower and the 30s token acquisition timeout in
live-auth.fixture.ts gets exceeded, causing cascading failures in the
last few test files.
Fixes:
- live-auth.fixture.ts: increase token waitForFunction timeout from 30s
to 60s, add retry loop (2 attempts with backoff), increase initial
navigation timeout to 45s, extract helper functions for clarity
- advisory-sync.e2e.spec.ts: increase page.goto timeout from 30s to 45s
for UI tests, add explicit toBeVisible wait on tab before clicking,
add explicit timeout on connectivity check API call
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scaffold connector plugins for DockerRegistry, GitLab, Gitea,
Jenkins, and Nexus. Wire plugin discovery in IntegrationService
and add compose fixtures for local integration testing.
- 5 new connector plugins under src/Integrations/__Plugins/
- docker-compose.integrations.yml for local fixture services
- Advisory source catalog and source management API updates
- Integration e2e test specs and Playwright config
- Integration hub docs under docs/integrations/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge three disconnected help surfaces (Stella mascot, Ctrl+K search,
Advisory AI chat) into one unified assistant. Mascot is the face,
search is its memory, AI chat is its voice.
Backend:
- DB schema (060/061): tips, greetings, glossary, tours, user_state
tables with 189 tips + 101 greetings seed data
- REST API: GET tips/glossary/tours, GET/PUT user-state with
longest-prefix route matching and locale fallback
- Admin endpoints: CRUD for tips, glossary, tours (SetupAdmin policy)
Frontend:
- StellaAssistantService: unified mode management (tips/search/chat),
API-backed tips with static fallback, i18n integration
- Three-mode mascot component: tips, inline search, embedded chat
- StellaGlossaryDirective: DB-backed tooltip annotations for domain terms
- Admin tip editor: CRUD for tips/glossary/tours in Console Admin
- Tour player: step-through guided tours with element highlighting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pack-shell title showed the raw packId like 'Pack pack-291b12b...'
which is unreadable. Now looks up the display name from the pack store
cache. Falls back to a truncated ID (first 12 chars) when no name is
available. Added PolicyPackStore.currentPacks() synchronous accessor
for the computed signal lookup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 6 pack detail components (dashboard, editor, rule-builder, yaml,
simulation, approvals) had :host background set to surface-inverse
(dark/black) or gradient variations of it. Fixed to surface-primary
(warm cream) to match the rest of the app. Also removed excessive
min-height: 100vh from all components.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pack cards completely restyled following the releases page pattern:
- Status badge (Active/Draft/Archived) with semantic colors
- Pack name with text-overflow ellipsis and title tooltip
- Version badge in mono font with subtle background
- Description and creation date in proper typography scale
- Decision capsules for actions (Edit, Simulate, Approvals, Delete)
matching the releases page capsule pattern with SVG icons
- Hover: brand-primary border glow + subtle shadow lift
- Delete button with app-confirm-dialog (danger variant)
Layout changed from multi-column grid to single-column list for
better readability (each card is a full-width row).
Empty state follows the releases empty state pattern with centered
SVG icon, title, description, and inline create form.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The cache contained API responses with packId (old format) but the
new mapper produces id. The store read these from cache, bypassing
the API, and sortPacks filtered them out (no id field) → empty page.
Fix: readCache() now detects stale entries (packId present, id missing)
and clears them, forcing a fresh API fetch through the mapper.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The backend returns PolicyPackSummaryDto with fields: packId,
displayName, createdAt, versions. The UI model expects: id, name,
description, version, status, etc.
Added mapPackSummary() to translate API fields to UI model fields.
The sortPacks filter was removing all packs because p.id was undefined
(the API field is packId, not id).
Also maps createPack response and sends displayName in the POST body
to match the backend's expected field name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VEX hub (13 files): fixed 2px/3px/4px border variants using text
color vars, border-color with text-secondary, background text-secondary.
Policy gates (6 files): fixed all swapped theme patterns including
backgrounds, text colors, borders, button backgrounds, and rgba colors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 13 VEX hub components had the same swapped theme pattern:
- text-heading used as backgrounds → surface-elevated
- surface-secondary used as text color → text-heading
- border-primary used as text color → text-primary
- text-primary used as backgrounds → surface-tertiary
- status-info used as button bg → btn-primary-bg
- Hardcoded rgba() colors → CSS variable references
- text/border color swaps in border declarations
168 occurrences fixed across: vex-hub-dashboard, vex-statement-search,
vex-statement-detail, vex-statement-detail-panel, vex-create-workflow,
vex-hub-stats, vex-consensus, vex-conflict-resolution, vex-hub,
ai-explain-panel, ai-justify-panel, ai-remediate-panel, ai-consent-gate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs caused the empty packs page:
1. When the API failed, catchError returned [] and writeCache() stored
it in sessionStorage. On next visit, readCache() returned [] (truthy),
so the store never re-fetched from the API — stuck on empty forever.
2. Fix: only cache successful non-empty API responses. Treat empty
cached arrays as cache misses so the store always re-fetches.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Theme fixes across 7 pack detail sub-components (dashboard, editor,
rule-builder, yaml, simulation, approvals, explain):
- Replace swapped CSS variables (text colors as backgrounds, etc.)
- Replace hardcoded rgba() colors with CSS variable references
- Replace btn backgrounds from status-info to btn-primary-bg
Pack ID guard:
- Filter out packs with missing/undefined IDs in sortPacks() to
prevent navigation to /packs/undefined/dashboard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add "Local UI Development" section with instructions for using the
docker-compose.dev-ui.yml override. Agents working on UI changes
should use this to avoid the slow volume-copy + restart cycle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three root causes for "create does nothing":
1. Wrong API base path: PolicyApiService used /api/policy (routed to
platform service via /api/ catch-all) instead of /policy/api (routed
to policy-gateway via nginx regex). Fixed API_BASE.
2. OnPush + plain fields: loading/packs/refreshing were plain booleans
and arrays. OnPush change detection ignored their mutations. Converted
all three to signals so template reactivity works automatically.
3. sortPacks crash: API returns packs with undefined modifiedAt/id
fields. localeCompare on undefined threw TypeError, preventing the
empty state from rendering. Added nullish coalescing fallbacks.
Also removed hardcoded fallback "Core Policy Pack" from the store —
it masked API failures and prevented the create form from appearing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each environment node now shows pill badges at the bottom:
- Deploying (blue, pulsing) — count of active deployments
- Pending (amber, pulsing) — count awaiting approval
- Failed (red) — count of failed deployments
- Total (muted) — historical deployment count
- Agent warning (!) — when no agent is assigned
Badges pulse with CSS animation (respects prefers-reduced-motion).
Native SVG <title> elements provide hover tooltips.
Backend enriches TopologyPositionedNode with deployment counts
from promotion path statuses and agent assignment data.
Also fixes duplicate environment IDs causing layout API 400 errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PolicyPackStore.refresh() now clears the sessionStorage cache before
fetching, so API responses aren't hidden behind stale cached data.
PolicyWorkspaceComponent fixes:
- Add ChangeDetectorRef.markForCheck() after every async state mutation
(OnPush was preventing UI updates from plain field assignments)
- Navigate to the new pack on successful creation
- Show clearer error when policy gateway is unreachable (status 0)
- Clear pack cache before navigation so the new pack appears
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename Releases to Deployments
- Replace custom toggle with standard segmented control (ctx__seg-btn
pattern from context-chips)
- Use stella-table with striped/hoverable classes
- Add sortable columns (Release, Status, Date) with sort arrows
- Add relative date formatting (2h ago, 3d ago, etc.)
- Add pagination via app-pagination component (5 per page)
- Add Event column showing eventType
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The workspace component had its own header (eyebrow 'Policy Studio ·
Workspace', h1 'Policy packs', lede) that duplicated the parent
pack-shell's 'Policy Pack Workspace' title. Removed it.
Moved the Refresh button from bottom-left footer to top-right actions
bar using secondary button styling. Removed min-height: 100vh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an environment is selected, the side drawer now shows a Releases
section with a Pending/Done toggle. Fetches from /api/v2/releases/activity
filtered by environment. Release name and version are clickable links to
the release detail and version pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>