- integration-hub-ui.component.spec: fix integrationId → id property
- orphan-revival-regression: fix index signature access for getViewMode
- integration-detail-page.spec: fix mock Integration type
- Install @vitest/browser-playwright for test runner
- Sprint 025 FE-CLN-004: DONE — build verified, cleanup confirmed clean,
test runner Karma→Vitest migration is infrastructure not regression
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend (Scanner .NET):
- New ScanPolicyEndpoints.cs with GET/POST/PUT/DELETE /api/v1/scan-policies
- In-memory ConcurrentDictionary storage (no migration needed)
- Auth: scanner:read for list, orch:operate for mutations
- Registered in Scanner Program.cs
Frontend (Angular):
- New scan-policy.component.ts with table view, inline create/edit form,
enable/disable toggle, dynamic rules (type/severity/action)
- Route added at /security/scan-policies in security-risk.routes.ts
Gateway route already exists in router-gateway-local.json.
Sprint 002: all 7 tasks now DONE.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
flicker, pack creation, export tooltip, audit guidance
Sprint A: Harbor fixture now returns realistic search results (7 repos)
and artifact digests (3 versions with tags). Release creation wizard
Step 2 now shows actual images to select.
Sprint B: Scan polling caps at 60 polls (3 min). Shows timeout banner
with guidance link to Scheduled Jobs and "Keep Waiting" button.
Sprint C: /console/profile route now renders InsufficientPermissions
component instead of 404. Shows user/tenant, guidance, and nav links.
Catches all 24 guard redirect dead-ends.
Sprint D: Event stream chip no longer flickers DEGRADED during context
reloads. Loading state treated as connected (transient, not error).
Sprint E: Policy Packs empty state now has inline Create Pack form.
Calls existing PolicyApiService.createPack() backend endpoint.
Sprint F: Diagnostics Export button shows disabled tooltip "Run a
diagnostic check first" when no results available.
Sprint G: Audit Log shows guidance text when all module counts are 0.
Lists automatically captured event types. Confirms audit is active.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Harbor fixture returns plain text, not JSON. The RegistrySearchEndpoints
deserialization crashed with an unhandled JsonException causing 500 on
every /api/v1/registries/images/search request. This blocked the release
creation wizard at Step 2 (Components).
Fix: catch JsonException and return empty results gracefully. The release
wizard now shows "no results" instead of silently failing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: AuthSessionStore.isAuthenticated is a computed signal that
returns false during token refresh ('loading' status). Since all routes
use canMatch guards that read isAuthenticated, a token refresh causes
ALL routes to fail guard evaluation simultaneously, redirecting the user
to random pages.
Fix: Add wasEverAuthenticated latch that stays true once set. During
transient 'loading' states, isAuthenticated returns true if the user
was previously authenticated — the session is being refreshed, not lost.
This eliminates the "phantom redirect" bug that made every page in the
app unstable (pages would load then silently navigate away after 1-5
seconds). Verified stable on /setup/identity-access and /evidence/audit-log
with 12-second wait after navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Policy Packs: show "No policy packs configured" with description and
link to overview when pack list is empty
- Shadow Mode: add prerequisite text below disabled Enable/View Results
buttons — "Shadow mode requires at least one active policy pack" with
link to Packs tab. Applied to both indicator and dashboard components.
- JobEngine: show guidance "No jobs have been submitted yet..." when all
counts are 0, auto-hides when jobs appear
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sprint 020 complete: scan ID mismatch, user ID display, feed status text,
route redirect race condition, exception scope mismatch, admin scope bypass.
All verified via Playwright on fresh install.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical fixes:
- Replace router.navigateByUrl() with Location.replaceState() in
PlatformContextUrlSyncService to prevent re-evaluating canMatch guards
during query param sync. This was causing random page redirects across
all routes when auth session signals hadn't settled yet.
- Fix exception scope mismatch: Authority issues 'exceptions:read' (plural)
but guards checked 'exception:read' (singular). Aligned to plural form.
- Fix admin scope bypass: guards checked 'admin' scope but token has
'ui.admin'. Now both are accepted as superuser bypass.
- Remove duplicate scope entries in description map.
UX polish (from fix agents):
- Integration detail: formatActor() truncates raw user ID hashes to
"User 9a2d0730..." instead of showing full 32-char hex string.
- Dashboard feed status: show "Not checked yet" instead of "0 healthy"
when no advisory source health checks have run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug: canSubmit computed() used plain string imageRef which computed()
can't track. Submit button was permanently disabled.
Fix: Changed imageRef to signal(''), updated template to use
[ngModel]="imageRef()" (ngModelChange)="imageRef.set($event)",
updated all class references to imageRef() for reads and .set() for writes.
Bug: Scan submit called /scanner/api/v1/scans/ (console nginx prefix)
instead of /api/v1/scans/ (gateway route).
Fix: Removed /scanner/ prefix from all API URLs.
KNOWN ISSUE: Scanner service needs identity envelope middleware (same
as Concelier fix) — ReverseProxy strips JWT, scanner returns 400
"tenant required". The scan submission reaches the scanner but auth
fails. Fix requires adding the same pre-auth envelope middleware
pattern to the Scanner service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The scan route was added to security.routes.ts which isn't loaded by the
app router. The app loads security-risk.routes.ts at /security/*. Added
the scan route to security-risk.routes.ts so /security/scan resolves.
Verified: Scan Image page loads at /security/scan with heading, image
input, and submit button. 111/111 canonical routes passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
S5-T02: Dashboard 3-column layout with real APIs
- CSS Grid: security posture (1/3) | environments + actions (2/3)
- Left column: Vulnerability Summary (from VULNERABILITY_API getStats()),
SBOM Health (from computed sbomStats), Advisory Feed Status (from
SourceManagementApi getStatus())
- Right column: mission summary strip (4 cards), promotion pipeline
(env card grid), environments at risk table, quick links
- Footer: platform health bar with status dots
- Real API calls with independent loading states and catchError defaults
- Refresh button re-fetches all data
- Responsive: collapses to single column on mobile
- Welcome guide still spans full width when no environments
- Removed old: reachabilityStats signal, nightlyOpsSignals signal,
NightlyOpsSignal interface, TitleCasePipe/UpperCasePipe imports
S5-T03: Security reports as downloadable exports
- New shared utility: download-helpers.ts (downloadCsv, downloadJson)
- Risk Report tab: "Export CSV" + "Generate PDF" (print-friendly CSS)
- VEX Ledger tab: "Export VEX Decisions" as JSON download
- Evidence Export tab: explainer + "Open Export Center" link
- Tests updated: 6 new test cases for export functionality
All 6 sprints now complete. Angular build: 0 errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sprint 4 — Sidebar restructure (S4-T01+T02):
5 groups: Release Control, Security, Operations, Audit & Evidence, Setup & Admin
Groups 4+5 collapsed by default for new users
Operations extracted from Release Control into own group
Audit extracted from Security into own group
groupOrder and resolveMenuGroupLabel updated
Approvals badge moved to section-level
Sprint 2 — Demo data badges (S2-T04+T05):
Backend: isDemo=true on all compatibility/seed responses in
PackAdapterEndpoints, QuotaCompatibilityEndpoints, VulnerabilitiesController
Frontend: "(Demo)" badges on Usage & Limits page quotas
Frontend: "(Demo)" badges on triage artifact list when seed data
New PlatformItemResponse/PlatformListResponse with IsDemo field
Sprint 6 — Audit emission infrastructure (S6-T01+T02):
New shared library: src/__Libraries/StellaOps.Audit.Emission/
- AuditActionAttribute: [AuditAction("module", "action")] endpoint tag
- AuditActionFilter: IEndpointFilter that auto-emits UnifiedAuditEvent
- HttpAuditEventEmitter: POSTs to Timeline /api/v1/audit/ingest
- Single-line DI: services.AddAuditEmission(configuration)
Timeline service: POST /api/v1/audit/ingest ingestion endpoint
- IngestAuditEventStore: 10k-event ring buffer
- CompositeUnifiedAuditEventProvider: merges HTTP-polled + ingested
Documentation: docs/modules/audit/AUDIT_EMISSION_GUIDE.md
Angular build: 0 errors. .NET builds: 0 errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sprint 4:
S4-T04: Empty state guidance for sparse pages
- Unknowns: graceful "No unknown components" with "Scan an Image" link
(replaces API error message)
- Evidence Capsules: enhanced with "Create your first release" guidance
S4-T05: Canonical route cleanup
- All /administration/* routes now redirect to /setup/* canonical
- /admin/* redirects to /setup instead of /administration
- 8 admin-only routes migrated to /setup/* (identity-providers, offline,
configuration-pane, security-data, workflows, ai-preferences)
- Sidebar nav config updated to /setup/identity-providers
- Settings routes updated to /setup/* targets
Sprint 6:
S6-T03: Record Decision as modal overlay
- Both decision-drawer and decision-drawer-enhanced converted from
sliding drawer to centered fixed-position modal (z-index 1000)
- Semi-transparent backdrop, click-outside-to-close, scrollable body
- Max width 480/520px, max height 90vh
S6-T04: Topology wizard skip agent step
- "Skip — assign agent later" button when no agents available
- Deploy Agent guidance with CLI command
- Next button shows "Next (without agent)" when skipping
- Done summary shows "None (skipped)" for agent
- agentSkipped signal in wizard service
Angular build: 0 errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Walked through the full vulnerability triage workflow as a security
engineer. Found the artifact workspace — the product's killer feature
(evidence-grade findings with reachability, attestations, policy gating,
delta comparison, deterministic replay, VEX decisions). Recorded a VEX
decision for CVE-2023-38545.
Critical UX findings:
- UX-D1: No "Scan" entry point anywhere in the UI — scanner exists
(2 containers) but has no discoverable trigger from the console
- UX-D2: Triage workspace (best feature) hidden under "Triage" label —
security engineers look for "Vulnerabilities" or "Findings"
- UX-D3: Record Decision dialog unreachable on smaller viewports —
needs proper modal overlay instead of in-page drawer
- UX-D4: Security Posture shows 0 findings while Triage has 1 active
HIGH finding — different data sources
Assessment: The triage artifact workspace is 10/10 UX. The discoverability
is 2/10. Three changes would transform the security engineer experience.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All medium fixes verified on live stack:
- Registry search: returns empty (no mock data) — confirmed
- Post-seal guidance: "What's next?" panel shows on release creation
- User ID display: truncated to "User 209d1257..."
- Mirror generate: shows failure status with retry guidance
- Wizard error handling: already implemented (was incorrectly logged)
Audit log remains at 0 events — this is a product gap, not a UI issue.
Services need to emit audit events (write path missing across modules).
MapAuditEndpoints() only exposes the query interface.
Topology wizard step 5 (Agent) is an expected fresh-install blocker.
Final score: 21 fixed, 2 low-priority UI issues, 2 product gaps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix#14 — User ID hash display:
Add formatActor() to bundle-version-detail that truncates raw hex
hashes to "User 209d1257..." instead of showing the full 32-char ID.
Short IDs, emails, and names pass through unchanged.
Fix#15 — Mirror generate-immediately silent failure:
When domain creation succeeds but bundle generation fails, show a
meaningful status message instead of silently swallowing the error:
"Generation failed — the domain was created but the initial bundle
could not be generated. You can retry from the mirror dashboard."
Fix#19 — Wizard error handling:
Already implemented — topology wizard sets wizard.error signal on all
CRUD failures and displays error banner. No additional changes needed.
Fix#21 — Post-seal guidance:
After sealing a release (source=release-create query param), show a
"What's next?" panel with links to: Promote to environment, Review
approvals, Back to versions. Uses branded action buttons.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The topology wizard creates environments and targets via POST /api/v1/environments
and POST /api/v1/targets. These were routed to JobEngine which doesn't have
the identity envelope middleware, causing 404 on ReverseProxy routes.
Fix: Add environment CRUD, target CRUD, and agent list endpoints directly
to Concelier's TopologySetupEndpointExtensions. These use the same
Topology.Read/Manage authorization policies that work with the identity
envelope middleware.
Routes updated:
- /api/v1/environments → Concelier (was JobEngine)
- /api/v1/agents → Concelier (new)
Topology wizard now completes steps 1-4:
1. Region: CREATE OK
2. Environment: CREATE OK
3. Stage Order: OK (skip)
4. Target: CREATE OK
5. Agent: BLOCKED (expected — no agents deployed on fresh install)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The identity envelope PostConfigure on JwtBearerOptions didn't work because
AddStellaOpsResourceServerAuthentication configures its own events that
override PostConfigure. The OnMessageReceived handler was only in the
TestSigningSecret branch, never in the OIDC discovery branch used in prod.
Fix: Add a middleware BEFORE UseAuthentication() that reads
X-StellaOps-Identity-Envelope headers, verifies HMAC-SHA256 signature
using Router:IdentityEnvelopeSigningKey (from router-microservice-defaults),
and sets HttpContext.User with claims from the envelope.
Also fixed: read signing key from Router:IdentityEnvelopeSigningKey config
path (matches the compose env var Router__IdentityEnvelopeSigningKey from
x-router-microservice-defaults).
Verified: Topology wizard "Create Region" now succeeds — Next button enables.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix#20 — Audit log empty:
Wire app.MapAuditEndpoints() in JobEngine Program.cs. The endpoint file
existed but was never registered, so /api/v1/jobengine/audit returned 404
and the Timeline unified aggregation service got 0 events.
Fix#22 — Registry search returns mock data:
Replace the catchError() synthetic mock fallback in searchImages() with
an empty array return. The release wizard will now show "no results"
instead of fabricating fake "payment-service" with "sha256:payment..."
digests. getImageDigests() returns an empty-tags placeholder on failure.
Fix#13 — Topology wizard 401 (identity envelope passthrough):
Add TryAuthenticateFromIdentityEnvelope() to Concelier's JwtBearer
OnMessageReceived handler. When no JWT bearer token is present (stripped
by gateway's IdentityHeaderPolicyMiddleware on ReverseProxy routes),
the handler reads X-StellaOps-Identity-Envelope + signature headers,
verifies the HMAC-SHA256 signature using the shared signing key, and
populates ClaimsPrincipal with subject/tenant/scopes/roles from the
envelope. This enables ReverseProxy routes to Concelier topology
endpoints to authenticate the same way Microservice/Valkey routes do.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Concelier:
- Register Topology.Read, Topology.Manage, Topology.Admin authorization
policies mapped to OrchRead/OrchOperate/PlatformContextRead/IntegrationWrite
scopes. Previously these policies were referenced by endpoints but never
registered, causing System.InvalidOperationException on every topology
API call.
Gateway routes:
- Simplified targets/environments routes (removed specific sub-path routes,
use catch-all patterns instead)
- Changed environments base route to JobEngine (where CRUD lives)
- Changed to ReverseProxy type for all topology routes
KNOWN ISSUE (not yet fixed):
- ReverseProxy routes don't forward the gateway's identity envelope to
Concelier. The regions/targets/bindings endpoints return 401 because
hasPrincipal=False — the gateway authenticates the user but doesn't
pass the identity to the backend via ReverseProxy. Microservice routes
use Valkey transport which includes envelope headers. Topology endpoints
need either: (a) Valkey transport registration in Concelier, or
(b) Concelier configured to accept raw bearer tokens on ReverseProxy paths.
This is an architecture-level fix.
Journey findings collected so far:
- Integration wizard (Harbor + GitHub App): works end-to-end
- Advisory Check All: fixed (parallel individual checks)
- Mirror domain creation: works, generate-immediately fails silently
- Topology wizard Step 1 (Region): blocked by auth passthrough issue
- Topology wizard Step 2 (Environment): POST to JobEngine needs verify
- User ID resolution: raw hashes shown everywhere
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The topology setup wizard calls /api/v1/regions, /api/v1/infrastructure-bindings,
/api/v1/pending-deletions, and target/environment readiness+validate endpoints
which are registered on the Concelier service. Without explicit gateway routes,
these fall through to the generic Microservice matcher which tries to find a
non-existent "regions" service, returning 503.
Added 6 Microservice routes forwarding topology API paths to
http://concelier.stella-ops.local. Both compose mount config and source
appsettings.json updated.
Verified: /api/v1/regions now returns 401 (auth required) instead of 503.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Advisory sources:
- Replace batch /check endpoint call (504 timeout after 30s for 42+
sources) with parallel individual checks in batches of 6. Progress
indicator now shows live "Checking (N/M)..." as each source completes.
Verified: 54/55 sources healthy on fresh install.
Dashboard:
- Remove the 5-element fallbackEnvironments array that was still
rendering fake environment cards. Empty array now correctly triggers
the setup guide on installs without PlatformContextStore environments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All sprint tasks marked DONE verified via Playwright canonical route sweep
(111/111 routes passing). Remaining active: Sprint 025 (BLOCKED on Node
heap exhaustion in full test suite).
New sprint: SPRINT_20260316_001 — First-Time User Experience Fixes (7 tasks).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 6 tasks DONE: consumer API endpoints, 4-step setup wizard UI,
dashboard and catalog integration, air-gap import API, E2E tests,
and documentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mirror.md: added section 8 covering the 4-step UI wizard flow, wizard
vs env var comparison table, and air-gap bundle import via UI and CLI.
architecture.md: added 6 consumer API endpoints (GET/PUT /consumer,
discover, verify-signature, import, import/status) to REST API section.
airgap-operations-runbook.md: cross-reference to UI import alternative.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Promotions: replace empty-state stub with operator landing surface
showing pipeline stages, prerequisites, and onboarding guidance.
Operations: unify naming across sidebar, breadcrumb, title, and H1
from "Platform Ops" to "Operations".
Playwright: add promotions and operations landing journey checks to
the retained first-time-user remediation and aggregate audit suites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Identity/Trust: replace developer jargon with operator-facing language
on trust overview, trust admin summary, and trust analytics. Add context-
aware error handling (404/503 vs generic) for fresh-install guidance.
Add navigation cards for Watchlist and Analytics in trust overview grid.
Integrations: replace raw alert() calls in test-connection and health-
check actions with inline feedback banners using Angular signals. Add
dismissible error banner for delete failures on integration detail.
Supporting fixes: admin notifications, evidence audit, replay controls,
notify panel, sidebar, route ownership, offline-kit, reachability,
topology, and platform feeds components hardened with tests and
operator-facing empty states.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire orch:operate scope into console bootstrap so the browser token can
execute release-control actions. Replace the silent-redirect fallback
with the canonical createBundle → publishVersion → materialize flow and
surface truthful error messages on 403/409/503. Add focused Angular
tests and Playwright journey evidence for standard and hotfix paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>