fix(platform): make topology probe fallback truthful

Signed-off-by: master <>
This commit is contained in:
master
2026-03-31 23:44:40 +03:00
parent 152c1b1357
commit 0d858ba9d1
13 changed files with 153 additions and 18 deletions

View File

@@ -373,7 +373,10 @@ public sealed class TopologyReadModelService
Status: hostStatus,
AgentId: first.AgentId,
TargetCount: group.Count(),
LastSeenAt: lastSeen);
LastSeenAt: lastSeen,
ProbeStatus: "not_installed",
ProbeType: null,
ProbeLastHeartbeat: null);
})
.OrderBy(host => host.RegionId, StringComparer.Ordinal)
.ThenBy(host => host.EnvironmentId, StringComparer.Ordinal)

View File

@@ -16,6 +16,8 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| B22-04 | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: shipped `/api/v2/security/{findings,disposition/{findingId},sbom-explorer}` read contracts, `platform.security.read` policy mapping, and migration `050_SecurityDispositionProjection.sql` integration. |
| B22-05 | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: shipped `/api/v2/integrations/{feeds,vex-sources}` contracts with deterministic source type/status/freshness/last-sync metadata and migration `051_IntegrationSourceHealth.sql`. |
| B22-06 | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: shipped `/api/v1/*` compatibility aliases for Pack 22 critical surfaces and deterministic deprecation telemetry for alias usage. |
| SPRINT_20260323_001-TASK-004 | DONE | Sprint `docs/implplan/SPRINT_20260323_001_BE_release_api_proxy_and_endpoints.md`: added `/api/v1/release-orchestrator/environments/*` compatibility endpoints for environment, target, and freeze-window CRUD using deterministic in-memory Release Orchestrator services. |
| SPRINT_20260331_002-TASK-003 | DONE | Sprint `docs/implplan/SPRINT_20260331_002_BE_host_infrastructure_and_inventory.md`: topology host projections now expose projection-derived `ProbeStatus`, `ProbeType`, and `ProbeLastHeartbeat` fields for Console host inventory views. |
| U-002-PLATFORM-COMPAT | DOING | Sprint `docs/implplan/SPRINT_20260218_004_Platform_local_setup_usability_hardening.md`: unblock local console usability by fixing legacy compatibility endpoint auth failures for authenticated admin usage. |
| QA-PLATFORM-VERIFY-001 | DONE | run-002 verification completed; feature terminalized as `not_implemented` due missing advisory lock and LISTEN/NOTIFY implementation signals in `src/Platform` (materialized-view/rollup behaviors verified). |
| QA-PLATFORM-VERIFY-002 | DONE | run-001 verification passed with maintenance, endpoint (503 + success), service caching, and schema integration evidence; feature moved to `docs/features/checked/platform/materialized-views-for-analytics.md`. |
@@ -49,4 +51,5 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| SPRINT_20260305_005-PLATFORM-BOUND-001 | DONE | Sprint `docs-archived/implplan/2026-03-05-completed-sprints/SPRINT_20260305_005_Platform_read_model_boundary_enforcement.md`: captured Platform runtime dependency inventory with explicit allowed runtime, migration-only, and prohibited coupling categories in architecture docs. |
| SPRINT_20260305_005-PLATFORM-BOUND-003 | DONE | Sprint `docs-archived/implplan/2026-03-05-completed-sprints/SPRINT_20260305_005_Platform_read_model_boundary_enforcement.md`: introduced `IPlatformContextQuery` and switched topology/security/integrations read-model services to explicit query contracts; DI now binds read-model contract separately from context mutation service. |
| SPRINT_20260305_005-PLATFORM-BOUND-004 | DONE | Sprint `docs-archived/implplan/2026-03-05-completed-sprints/SPRINT_20260305_005_Platform_read_model_boundary_enforcement.md`: documented runtime boundary policy, migration/runtime separation, and allowlisted exceptions in Platform dossiers. |
| SPRINT_20260331_006-PROBE-HYGIENE | DONE | Sprint `docs-archived/implplan/SPRINT_20260331_006_Platform_probe_truthfulness_and_topology_test_hygiene.md`: made topology probe fields truthful (`not_installed` fallback), added topology-only Angular/Vitest verification target, and removed topology-slice verification noise from the maintained test path. |

View File

@@ -68,6 +68,12 @@ public sealed class TopologyReadModelEndpointsTests : IClassFixture<PlatformWebA
Assert.Equal(
hostsFirst.Items.Select(item => item.HostId).ToArray(),
hostsSecond!.Items.Select(item => item.HostId).ToArray());
Assert.All(hostsFirst.Items, item =>
{
Assert.Equal("not_installed", item.ProbeStatus);
Assert.Null(item.ProbeType);
Assert.Null(item.ProbeLastHeartbeat);
});
var agentsFirst = await client.GetFromJsonAsync<PlatformListResponse<TopologyAgentProjection>>(
"/api/v2/topology/agents?limit=20&offset=0",

View File

@@ -114,11 +114,21 @@
]
}
},
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"configDir": ".storybook",
"browserTarget": "stellaops-web:build",
"test-topology": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "tsconfig.spec.topology.json",
"buildTarget": "stellaops-web:build:development",
"runner": "vitest",
"runnerConfig": "vitest.codex.config.ts",
"setupFiles": ["src/test-setup.ts"]
}
},
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"configDir": ".storybook",
"browserTarget": "stellaops-web:build",
"compodoc": false,
"port": 6006
}

View File

@@ -10,6 +10,7 @@
"analyze:source-map": "ng build --source-map && npx source-map-explorer dist/stellaops-web/browser/*.js",
"watch": "ng build --watch --configuration development",
"test": "ng test --watch=false",
"test:topology": "ng run stellaops-web:test-topology --watch=false",
"test:watch": "ng test",
"test:ci": "npm run test",
"test:e2e": "playwright test",

View File

@@ -364,8 +364,7 @@ export class ReachabilityWhyDrawerComponent {
if (!this.open()) return;
if (!this.component()) return;
void this.refresh();
},
{ allowSignalWrites: true }
}
);
}

View File

@@ -581,7 +581,7 @@ function severityToStatus(s: string): 'error' | 'warning' | 'info' | 'neutral' {
display: inline-flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, var(--color-brand-soft, rgba(59,130,246,0.12)) 75%, transparent);
background: var(--color-brand-soft, rgba(59,130,246,0.12));
color: var(--color-text-link, var(--color-brand, #3b82f6));
font-size: 1rem;
font-weight: 700;
@@ -896,7 +896,7 @@ export class TopologyEnvironmentDetailPageComponent {
this.context.initialize();
effect(() => {
this.helperCtx.setScope('topology-environment-detail', this.helperContexts());
}, { allowSignalWrites: true });
});
this.destroyRef.onDestroy(() => this.helperCtx.clearScope('topology-environment-detail'));
this.route.paramMap.subscribe(params => {
const id = params.get('environmentId') ?? '';

View File

@@ -689,7 +689,7 @@ export class TopologyHostsPageComponent {
effect(() => {
this.helperCtx.setScope('topology-hosts', this.helperContexts());
}, { allowSignalWrites: true });
});
this.destroyRef.onDestroy(() => this.helperCtx.clearScope('topology-hosts'));
}

View File

@@ -42,8 +42,8 @@ import { RouterOutlet } from '@angular/router';
width: 42px;
height: 42px;
border-radius: var(--radius-md);
background: color-mix(in srgb, var(--color-brand-primary) 10%, var(--color-surface-primary));
border: 1px solid color-mix(in srgb, var(--color-brand-primary) 25%, var(--color-border-primary));
background: var(--color-brand-soft, rgba(59, 130, 246, 0.12));
border: 1px solid rgba(59, 130, 246, 0.2);
color: var(--color-brand-primary);
flex-shrink: 0;
}

View File

@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec-topology",
"types": [
"vitest/globals"
]
},
"include": [
"src/app/core/testing/**/*.spec.ts",
"src/**/*.d.ts",
"src/test-setup.ts"
],
"exclude": [
"src/**/*.e2e.spec.ts"
]
}

View File

@@ -6,11 +6,6 @@ export default defineConfig({
environment: 'jsdom',
setupFiles: ['src/test-setup.ts'],
pool: 'threads',
poolOptions: {
threads: {
singleThread: true,
},
},
fileParallelism: false,
isolate: false,
maxWorkers: 1,