- Remove local filter bar (Search/Type/Health) — use global header
context (Region/Env/Stage) as single source of filtering
- Pass global context selections to layout API — graph re-fetches and
ElkSharp re-lays out only the filtered environments
- Fix backend: skip promotion edges where source or target environment
is outside the filtered set (was causing 400 errors)
- Replace below-graph detail zone with 360px right side drawer that
doesn't scroll away from the graph
- Stats badges (regions/environments/paths) moved to overlay on graph
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Increase LayerSpacing from 60 to 120 and NodeSpacing from 40 to 60
for wider promotion arrows between environments
- Increase CompoundPadding from 30 to 40 for better region container
separation
- Replace inline edge labels with tooltip callout pattern: truncated
text in a background box with dashed leader line to the edge
- Edge labels capped at 30 chars with ellipsis
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 3 fragmented environment views (2 hardcoded stubs + tables component)
and D3.js force-directed map with a single unified topology page at
/environments/overview. The page renders an interactive SVG graph using
ElkSharp compound layout (regions as parent containers, environments as
child nodes, promotion paths as directed edges with gate labels).
Backend: new GET /api/v2/topology/layout endpoint that builds ElkGraph
from topology read model, runs ElkSharp compound layout, returns enriched
positioned nodes and routed edges.
Frontend: topology-graph.component.ts (SVG renderer with zoom/pan/select),
topology-graph-page.component.ts (filter bar + graph + detail side panel).
Deleted: environments-list-page, platform-setup-regions-environments-page,
topology-map-page, topology-regions-environments-page. Routes consolidated
from ~12 paths to 6 with backward-compat redirects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CRUD endpoints for script variables with validation and diff-compatible
models (ScriptEndpoints, ScriptApiModels, IScriptService, InMemoryScriptService).
Update PlatformScopes and PlatformPolicies for script.read/write permissions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add UseIdentityEnvelopeAuthentication() to Authority service, fixing
/console/tenants 403. Authority was the last service missing it.
- Add ui.preferences.read and ui.preferences.write to OIDC scope request
in config.json and PlatformServiceOptions, fixing preferences/language 403.
The scopes existed in Authority but were never requested by the frontend.
All services now have identity envelope middleware:
Gateway, Platform, Scanner, JobEngine, Timeline, Integrations,
Concelier, Authority (8/8 complete)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Platform was the ONLY service missing UseIdentityEnvelopeAuthentication().
Sprint 008 added it to Scanner, JobEngine, Timeline, Integrations, Concelier
but missed Platform. This caused ALL context API calls to return 403:
- /api/v2/context/regions
- /api/v2/context/environments
- /api/v2/context/preferences
The gateway strips JWT and sends signed identity envelope headers instead.
Without the middleware, Platform couldn't read the scopes from the envelope,
so every authorization check failed.
This also fixes the "Events: DEGRADED" SSE status indicator since the
context store's error was caused by these 403 responses.
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>
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>
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 gateway service was a redundant deployment of the same
StellaOps.Gateway.WebService binary already running as router-gateway.
It served no unique purpose — all traffic is handled by router-gateway
(slot 0). This removes the container, its route table entries, nginx
proxy blocks, health/quota stubs, and redirects STELLAOPS_GATEWAY_URL
to router.stella-ops.local so the Angular frontend resolves API base
URLs through the canonical frontdoor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add resource limits (heavy/medium/light tiers) to all 59 .NET services
- Add .NET GC tuning (server/workstation GC, DATAS, conserve memory)
- Convert FirstSignalSnapshotWriter from 10s polling to Valkey pub/sub
- Convert EnvironmentSettingsRefreshService from 60s polling to Valkey pub/sub
- Consolidate GraphAnalytics dual timers to single timer with idle-skip
- Increase healthcheck interval from 30s to 60s (configurable)
- Reduce debug logging to Information on 4 high-traffic services
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plugin assemblies loaded via PluginHost into isolated AssemblyLoadContexts
produce distinct types even from the same DLL. When AppDomain.GetAssemblies()
returns both Default and plugin-ALC copies, DI registration and IOptions<T>
resolution silently fail (e.g. ValkeyTransportOptions defaulting to localhost).
Applied AssemblyLoadContext.Default filter to all 7 assembly discovery sites:
- MessagingServiceCollectionExtensions (transport plugin scan)
- StellaRouterIntegrationHelper (transport plugin loader)
- Gateway.WebService Program.cs (startup transport scan)
- GeneratedEndpointDiscoveryProvider (endpoint provider scan)
- ReflectionEndpointDiscoveryProvider (endpoint attribute scan)
- ServiceCollectionExtensions (schema provider scan)
- MigrationModulePluginDiscovery (migration plugin scan)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>