Repository navigation
refactor(slm-frontend): derive the monitoring long tail from the generated schema (#13138) - #13159
Merged
Merged
Conversation
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
…odels from the generated schema (#13138) Derives 47 hand-declared SLM frontend types from components['schemas'][...]: the whole monitoring/observability long tail in types/api-responses.ts and the NPU, maintenance, service, role, blue-green and external-agent shapes in types/slm.ts. Drift the derivation exposed and corrected: - BlueGreenStatus omitted 'monitoring' (models/database.py:581), the post-deploy health-watch state set at services/blue_green.py:780. - NodeRole omitted 'docker' (services/role_registry.py:372), a role GET /deployments/roles has always returned; NODE_ROLE_METADATA gains the matching entry it is documented to mirror. - RoleInfo omitted ansible_role; NodeService omitted endpoint_path, port, protocol and is_discoverable; BlueGreenDeployment omitted health_failure_threshold, health_failures, monitoring_started_at and post_deploy_monitor_duration. - temperature_celsius, memory_bytes and the blue-green timestamps are optional AND nullable; three helpers tested only for null. - NPUCapabilities.deviceType is copied verbatim out of an external worker's /health payload (api/npu.py:83), so the four-value union was unverifiable; all three renderers already fall back to the raw string. - BlueGreen shapes were declared twice (types/slm.ts and types/api-responses.ts) for the same wire models; now one derivation plus aliases.
…ame the monitoring view-models (#13138) A blue-green deployment in the 'monitoring' status (services/blue_green.py:780, held for up to post_deploy_monitor_duration seconds) matched none of the four bgStats buckets and fell through getStatusClass to the neutral gray default, so it vanished from the dashboard entirely. Adds it to the in-flight set and the in-flight badge, backed by 4 tests that fail against the pre-fix view. Also drops the 'as BlueGreenDeployment[]' cast that bridged the two independent hand-written copies of the same wire model, now that both derive from one schema. View-models that shadow a generated schema while deliberately remapping it are renamed rather than derived, so the collision cannot come back: LogEntry -> LogRow (LogViewer.vue, maps severity->level), LogEntry -> DeploymentLogMessage (DeploymentLogViewer.vue, a WebSocket envelope), SystemMetrics -> SystemMetricsViewModel and DashboardOverview -> DashboardViewModel (usePrometheusMetrics.ts, synthesised from fleet_metrics), RoleInfo -> WizardRoleOption (SetupWizardView.vue, builds a display_name the contract does not have). DeploymentWizard.vue drops its own RoleInfo copy for a projection with derived member types, and normalises the optional 'dependencies' its template indexes.
…collapsing the duplicate PlaybookInfo (#13138) PlaybookInfo was declared identically in InfrastructureWizard.vue and InfrastructureView.vue; both now import one derived definition in types/slm.ts. Both copies omitted 'tags' and typed 'category' as a bare string where the contract has the PlaybookCategory enum. PlaybookExecution gains the PlaybookStatus enum and the correct optionality of 'output'. TimeConfig (GeneralSettings.vue) derives from the /settings/time model.
mrveiss
force-pushed
the
issue-13138-monitoring
branch
from
July 31, 2026 20:36
0a41e96 to
bd7629f
Compare
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #13161 — the monitoring long-tail slice of #13138. #13138 itself stays OPEN: 4 shapes remain, 3 of them still blocked on #13145 and the 4th an alias-of-derived counting artifact. Partial delivery never closes the parent.
Thinking Path
Fourth slice of #13138, after #13137 (39), #13146 (18) and #13152 (23). Target was the monitoring long tail —
types/api-responses.ts(~30) and the rest oftypes/slm.ts(~20).Measured on the tip with the three rules the prior slices established (require a declaration body · exclude
import { type X }· a name counts as remaining if any declaration lacks acomponents['schemas'][…]body): 309 schemas, 422 hand-declared names, 132 collisions, 78 derived, 54 still hand-written. One unit above the 53 reported by slice 3:ThreatSummaryatSecurityView.vue:43istype ThreatSummary = ThreatSummaryType, an alias of the derived import — remaining by the strict per-name rule, already correct in substance. Effective 53, so the count reproduces.Treated every disagreement as a finding and established which side was wrong before making it compile. Four defects fell out, all with the server right.
Two more view-models found, exactly where the brief predicted. The four already known (
DashboardOverview/SystemMetricsinusePrometheusMetrics.ts,LogEntry×2) plus a fifth:RoleInfoatSetupWizardView.vue:594declares adisplay_namethe contract does not have —loadRolesbuilds it fromdescription, because the backend already folds the registry'sdisplay_nameintodescription(api/deployments.py:157-160). Deriving it would have swapped a working local shape for an unrelated wire shape. All five are renamed, not derived, so the collision cannot come back. A sixth candidate,RoleInfoatDeploymentWizard.vue:12, is a straight projection rather than a remap, so it drops its copy for one with derived member types.What Changed
53 → 4 remaining. 50 shapes derived; 3 of the 4 left are blocked on #13145, the 4th is the
ThreatSummaryalias.Defects found (server right in every case)
1. A blue-green deployment in post-deploy monitoring showed no state at all.
BlueGreenStatusomitted'monitoring'— the health-watch state set atservices/blue_green.py:780and held for up topost_deploy_monitor_durationseconds (default 1800). It matched none of the fourbgStatsbuckets (DeploymentsView.vue:202-205) and fell throughgetStatusClassto the neutral gray default, so a deployment being actively health-watched vanished from every tile and its badge claimed nothing was happening. The hand-written type also had nohealth_failures,health_failure_threshold,monitoring_started_atorpost_deploy_monitor_duration, so there was nothing to render even had it been counted. 4 new tests, confirmed failing against the pre-fix view.2.
NodeRolewas missing'docker'.constants/node-roles.tsstates "Source of truth:role_registry.py:DEFAULT_ROLES" and "Keep in sync", but the registry has 21 roles and the union had 20 —docker(_INFRA_ROLES,role_registry.py:372) has always been returned byGET /deployments/roles.Record<NodeRole, RoleMetadata>forced the matching metadata entry once the union was corrected.3.
PlaybookInfowas declared identically twice (InfrastructureWizard.vue:19,InfrastructureView.vue:20) — one backend change, two places to drift from. Same class as the/security/*duplicate in #13146. Both omittedtagsand typedcategoryas a barestringwhere the contract has thePlaybookCategoryenum.4.
BlueGreenDeployment/BlueGreenCreate/BlueGreenListResponsewere each declared twice for the same wire models, intypes/slm.tsandtypes/api-responses.ts, withDeploymentsView.vue:335bridging them viaas BlueGreenDeployment[]— so a divergence would have been silently cast away. Now one derivation plus aliases, and the cast is gone.Fields the frontend type could not reach
RoleInfo.ansible_role·NodeService.endpoint_path/port/protocol/is_discoverable·BlueGreenCreate.health_failure_threshold/post_deploy_monitor_duration(so the New Blue-Green modal could never configure post-deploy monitoring) ·PlaybookInfo.tags.Optional-and-nullable, declared required
temperature_celsius(NPUPerformanceMetrics.vue:54,61tested only=== null, so an absent reading renderedundefined°Cin the "cool" colour),memory_bytes(NodeServicesPanel.vue:145— the body already handledundefined, only the signature disagreed;useNodeServices.ts:56used!== null), the blue-green timestamps (DeploymentsView.vue:575),RecentError.resolved_at/resolved_by,MetricsSummary's three nullables,FleetMetricsResponse.timestamp,SystemHealthResponse.last_check,AlertItem.node_id/hostname,PlaybookExecution.output.NPUWorkerConfig.assigned_modelsisdefault_factory=listand so omittable —toggleModelindexed it unguarded, now normalised at the load site.Widening, resolved per type
RestartAllServicesRequest.category(pattern="^(autobot|system|all)$"),NodeService.categoryErrorStatistics.trend(3 branches,errors.py:245-273),ErrorHealthResponse.status(3 branches,:509-518),BlueGreenStatus,NPULoadBalancingConfig.strategy(enum atschemas.py:2078-2085),RoleInfo.name/categoryNPUWorkerConfig.failure_action— sole construction site is the four-option<select>atNPUDetailsPanel.vue:383-386NPUCapabilities.deviceType— copied verbatim out of an external worker's/healthpayload (api/npu.py:83); all three renderers already fall back to the raw stringRoleInfo.dependencies(deployments.py:166passes[]unconditionally)Bare
dictserver-sideRestartAllServicesResponse.resultsisList[Dict](schemas.py:920), so the contract can only say{ [key: string]: unknown }[]. Derived for the scalars and intersected to pinRestartServiceResult, whose keys are built atapi/services.py:892-897and always include theis_slm_agentthe UI reads.A third trap for the next slice
Omit/Pickcollapse (#13152) andrequired-means-defaulted (#13152) both held. A new one:A[] & B[]keeps bothmapsignatures and TypeScript resolves the callback against the first, sobase & { items: Narrow[] }silently types the callback parameter as the WIDE element — that is exactly howuseNodeServices.ts:52ended up mappingcategoryasstringwith no error at the definition site. Writing{ items: Narrow[] } & basepicks the narrowed element. All list envelopes here use that order, and the rule is recorded onRoleListResponse.Deferred, with reasons
NodeCreate,NodeUpdate,ConnectionTestRequest. Unchanged; a product decision.ThreatSummary(SecurityView.vue:43) —type ThreatSummary = ThreatSummaryType, an alias of the derived import inside a block of five such aliases. Correct in substance; renaming it churns the file for a measurement artifact.BlueGreenCreate's two new knobs are now reachable but get no UI control here — that is a feature, not contract derivation.Verification
All measured in a worktree synced to
Dev_new_gui, rebased ontoa1fc379d7mid-work after another session pushed #13153, then re-verified.npm run type-check— clean (vue-tsc --noEmit -p tsconfig.json, no output).npx vitest run— 27 files / 219 tests passed, up from 26 / 215 (+4 blue-green tests). Baseline taken against acp-swapped pristinesrc/restored fromgit archive, nevergit stash.DeploymentsView.vueback in:expected '0' to be '1'for the active tile, andexpected … to contain 'bg-blue-100 text-blue-800'for the badge. The two regression tests (pre-existing in-flight statuses, terminal status) pass both before and after.npm run lint— 16 problems, 0 errors, 16 warnings, identical to the pristine baseline apart from one line-number shift inDeploymentWizard.vue(107 → 123) caused by an added doc comment: same file, same rule, same finding.git diff --exit-code src/types/generated/api.ts— clean, soverify-generated-types-slmholds.sudo.Model Used
claude-opus-5