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>
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>
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>
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>
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>
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>