Skip to content

feat: Operations Passwords tab and Service Health (#1635, layer 1 of 3) - #1636

Merged
JayVDZ merged 10 commits into
mainfrom
feature/operations-health
Sep 6, 2026
Merged

feat: Operations Passwords tab and Service Health (#1635, layer 1 of 3)#1636
JayVDZ merged 10 commits into
mainfrom
feature/operations-health

Conversation

@JayVDZ

@JayVDZ JayVDZ commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Layer 1 of #1635 (plan: engineering/plans/doing/PASSWORD_PIPELINE_CONVERGENCE.md). Bottom of a three-layer stack; layers 2 (Password Delivery Service) and 3 (one password operation) follow on top.

  • Passwords tab on Operations. The unreleased /admin/password-synchronisation page moves under Administration > Operations as a fourth tab beside Queue, History and Schedules, badged with parked plus expired changes. Deep links carry metaverseObjectId and connectedSystemId on the Operations route.
  • Service heartbeats. jim.worker and jim.scheduler write a ServiceHeartbeats row every 5 s (instance, host, version, started, last seen, current work) alongside the file touch the container health check already uses. New migration AddServiceHeartbeats.
  • Service Health on Operations. A strip above the tabs with one card per expected service (Worker sync loop, Scheduler) plus a Live updates card for the notification relay: state (Running, Stale, No progress, Not seen), current work and how long, last seen, host, version with a skew warning against the portal's version. Polled every 10 s; the same tick refreshes the Passwords badge.
  • Administrator banner. Above page content on every page for administrators only, when a service is Not seen or has made No progress, naming the service and linking to Operations and Logs. Hidden on Operations itself. Red dot on the Operations tile of the Administration index.
  • Parity. GET /api/v1/system/health (Administrator, Cache-Control: no-store) and Get-JIMServiceHealth (per-service objects, or -Summary). Docs: new docs/configuration/operations.md, docs/powershell/system.md, API index, deployment health section. Changelog entries under Unreleased.
  • Password delivery is deliberately not an expected service in this layer; layer 2 adds it when its loop exists to report, so no deployment carries a permanent red card or banner meanwhile. A heartbeat from an unexpected service is still shown.

Verification

  • dotnet build JIM.sln: 0 warnings, 0 errors. dotnet test JIM.sln: green (see checks).
  • Pester System.Tests.ps1 and module tests: green. Lint-Changelog.ps1: passes.
  • Runtime on the devcontainer stack: rebuilt worker, scheduler and web images; ServiceHeartbeats rows for WorkerSync and Scheduler present with ages of 1 to 7 s over a 20 s sample; migration applied cleanly; web container healthy with the new components in the image. No signed-in screenshot: the review browser cannot reach the devcontainer's ports.

Closes nothing on its own; #1635 closes when the stack lands.

🤖 Generated with Claude Code

JayVDZ and others added 8 commits September 5, 2026 09:20
…ns service health (#1635)

Implementation plan for three stacked layers (Operations and service health; a Password Delivery Service off the worker task queue; convergence of Set Password and Synchronise Password), and the PRD amendment recording the decisions that change requirements 21, 25 and 31.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rations (#1635)

The standalone /admin/password-synchronisation page (unreleased) is dropped
per decision D9. Its body moves to OperationsPasswordsTab, hosted as a fourth
tab on Operations with a key icon and an amber badge of parked plus expired
changes. The metaverseObjectId and connectedSystemId deep links keep working
on the Operations route, and clearing a deep-linked filter also drops it from
the URL so it does not return when the tab is re-created on a switch back.

Deep links and wording updated: the Metaverse Object panel's queue link, the
Connected Systems list indicator tooltip, the Administration index (tile
removed; Operations tile mentions password delivery), the concepts doc and
the unreleased changelog entry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each background service now writes a ServiceHeartbeat row every 5 seconds from the same place it touches
its container health-check file: the Worker's synchronisation loop (with its in-flight tasks as CurrentWork)
and the Scheduler (both while waiting for readiness and, in heartbeat-sized slices of its 30-second wait,
in its main loop). SystemHealthServer (JimApplication.SystemHealth) derives Running / Stale / NoProgress /
NotSeen per service from the newest row, with the worst state as the report's Overall, for the Operations
page, REST API and PowerShell to read.

- JIM.Models.Operations: JimService, ServiceHeartbeat, ServiceHealthState, ServiceHealth, ServiceHealthReport
- ISystemRepository: single-statement ON CONFLICT upsert, newest-per-service read, per-service prune
- Migration AddServiceHeartbeats (unique index on Service, InstanceId)
- ServiceHeartbeatWriter: throttled, prunes its service's day-old rows on first write, never throws a
  database failure into the host loop (first failure at Warning, repeats at Debug, recovery at Information)
- JimVersion.Current shared by Web, Worker and Scheduler; HealthController uses it
- TaskTask carries a dispatch-time description and start so the heartbeat costs no extra reads

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…1635)

GET /api/v1/system/health (Administrator) returns the ServiceHealthReport
the Operations page reads: overall state, web version, generated-at and
one entry per service with state, reason, current work and instance
detail, enums as string names, Cache-Control: no-store.

Get-JIMServiceHealth emits one JIM.ServiceHealth object per service, or a
single JIM.ServiceHealthSummary with -Summary whose Overall is the worst
state present, for monitoring scripts.

Docs: new configuration/operations.md describing the Operations page and
its Service Health strip (states, thresholds, banner, version skew, REST
and PowerShell equivalents), the cmdlet reference, and the endpoint noted
in the API overview and deployment health table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Password delivery has no heartbeat writer until the Password Delivery Service lands in the next layer, so listing it as expected put a permanent "never reported" card and a permanent administrator banner on every deployment. The report now covers the expected services (Worker sync loop, Scheduler) plus any service that has actually reported, in enum order; the next layer adds password delivery to the expected list. Docs describe three cards accordingly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One card per background service at the top of Operations (Worker · Sync,
Worker · Passwords, Scheduler) plus a Live updates card for the notification
relay, polled by the page every ten seconds alongside the Passwords badge; a
banner above every page for administrators when a service is not seen or a
task has stalled, hidden on Operations itself; a red dot on the Operations
tile of the Administration index under the same verdict. MainLayout's footer
version now comes from JimVersion.Current so it matches what the strip
compares service versions against.

A password delivery service that has never reported beside a reporting Worker
is treated as a version gap rather than an outage: the card says so, the
banner stays quiet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The read model now decides which services are expected, so the banner's special case for a never-reported password delivery loop is redundant. Until a delivery loop is in the report, the synchronisation loop down is the whole Worker down, and the banner says so in those words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…form cards (#1635)

Replace ServiceHealthState with two enums: ServiceHealthStatus (Healthy,
Degraded, Unhealthy; ordered by severity, Overall is the max) and
ServiceHealthCondition (Heartbeating, HeartbeatOverdue, Stalled, NoHeartbeat,
NeverStarted), with the thresholds unchanged and StatusOf as the single
condition-to-status mapping. Reason sentences become plain words ("Heartbeat
3 seconds ago", "No heartbeat for 4 minutes", "Never started"). The REST DTO
carries status and condition, the cmdlet Status and Condition, and the docs,
Pester and API tests follow.

Rebuild ServiceHealthStrip as a panel: a header with the title, a worst-first
summary ("1 service unhealthy, 1 degraded") and the Live updates indicator
(no longer a card), over a CSS grid of identical four-slot cards (name and
status pill; activity; condition; host, version, uptime). The pill is the
only coloured element; version skew is a "differs from portal" chip rather
than amber text. The banner sentences and the Administration index red dot
(Unhealthy, or Degraded by Stalled) are unchanged in behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@JayVDZ
JayVDZ enabled auto-merge (squash) September 6, 2026 17:18
@JayVDZ
JayVDZ merged commit 0b754e9 into main Sep 6, 2026
17 checks passed
@JayVDZ
JayVDZ deleted the feature/operations-health branch September 6, 2026 17:32
JayVDZ pushed a commit that referenced this pull request Sep 6, 2026
…ivery-service

Three-way merge using the layer 1 tip (63bbbfe) as the base, because #1636 squash-merged and
git's default merge-base predates it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mw5XPncvpoN78zeazx5jgi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants