Skip to content

feat: one Set Password operation over the delivery queue; Synchronise Password withdrawn (#1635, layer 3 of 3) - #1639

Open
JayVDZ wants to merge 7 commits into
feature/operations-health-stack-password-delivery-servicefrom
feature/operations-health-stack-password-pipeline
Open

feat: one Set Password operation over the delivery queue; Synchronise Password withdrawn (#1635, layer 3 of 3)#1639
JayVDZ wants to merge 7 commits into
feature/operations-health-stack-password-delivery-servicefrom
feature/operations-health-stack-password-pipeline

Conversation

@JayVDZ

@JayVDZ JayVDZ commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Layer 3 of #1635 (plan: engineering/plans/done/PASSWORD_PIPELINE_CONVERGENCE.md), stacked on #1638 and #1636. Closes #1635 when the stack lands.

  • One password operation. PasswordSynchronisationServer.SetPasswordAsync(SetPasswordRequest) replaces both the immediate set-password path and the separate synchronise path. A request names accounts (Connected System Object ids) or names none, meaning every Connected System configured for Password Synchronisation. Both origins write the same queue rows, the same Activity shape (parent per change, child per system) and are delivered by the Layer 2 service.
  • Explicit rows carry their account and the enable-account decision, need no Password Synchronisation configuration, use default retry settings where none exists, and are delivered even where a configuration is switched off (decision D1). Propagated rows behave exactly as before. Migration AddPasswordChangeOrigin.
  • One delivery core. The open channel, security refusal, set and classify sequence lives once in PasswordDeliveryCore, used by the delivery lane and by initial password provisioning. The immediate ConnectedSystemServer set-password methods, the queue forwarders and their result DTOs are gone.
  • Portal. The person page has one administrator-only Password tab: attention strip with Retry for parked changes, the Set Password card with the capable-account count, still-to-be-delivered rows with Retry and Stop trying, and history with a Set or Propagated chip. SetPasswordDialog keeps its composition (picker, generate, copy while masked, expiry, enable) and its result stage now follows the queue live: Set, Retrying with next attempt and Stop trying, Parked with the target's words and guidance, Try another password. Synchronise Password and the Actions tab are removed. The account page uses the same dialog with one target.
  • REST and PowerShell. POST /api/v1/metaverse/objects/{id}/password is Set Password: password, optional connectedSystemObjectIds, expiryBehaviour (defaults differ by mode), enableAccount (named accounts only), wait (default 10 s with accounts, 0 when propagating); per-target outcome including failureReason, 200 settled or 202. The account-scoped endpoint is a one-target wrapper with the same response. Set-JIMMetaverseObjectPassword gains -ConnectedSystemId <int[]> naming accounts, the propagate default when none is named (decision D5), -Wait, and the outcome shape; -AllAccounts and Sync-JIMMetaverseObjectPassword are withdrawn; Set-JIMConnectedSystemObjectPassword returns the same shape.
  • Wording (decision D4). Set Password no longer claims to store nothing: JIM holds the password encrypted only until each system has it, and keeps a refused one so it can finish the job. Dialog, endpoint remarks and docs/concepts/passwords.md say so; the "two ways to give somebody a password" section is now one operation with two target modes.
  • Service Health cards, after review: uptime sits on the heartbeat line; host, version and instance id move behind a per-card details control with copy buttons; the version skew chip stays visible; no card has an empty slot.
  • The person's password history is a day-grouped timeline in the Changes tab's visual language: one entry per change, an outcome pill per Connected System in the same vocabulary as Service Health, words only where something needs them, Retry and Stop trying inline.
  • Changelog amended under Unreleased so 0.15.0 describes the final shape; the plan document moves to done.

Verification

  • dotnet build JIM.sln: 0 warnings, 0 errors; dotnet test JIM.sln: green (see checks). RequiresPostgres fixtures for claims, origins and outcomes run against a scratch database.
  • Pester: 1834 passed across the module; Lint-Changelog.ps1 and Lint-DocExamples.ps1 pass.
  • Runtime on the devcontainer stack: an explicit set for one person on Panoply AD was claimed within half a second and Set within a second; the Password tab, the dialog's result stage and the history chip verified in the browser; scenario 20's latency steps passed on Layer 2 and its script now uses Set-JIMMetaverseObjectPassword.

🤖 Generated with Claude Code

JayVDZ and others added 7 commits September 5, 2026 13:45
…d a shared delivery core (#1635)

Set Password and Synchronise Password converge on PasswordSynchronisationServer.SetPasswordAsync(SetPasswordRequest):
Targets null propagates to every Connected System configured for Password Synchronisation (unchanged behaviour,
Origin Propagated, EnableAccount never carried); a list of Connected System Object ids queues one Explicit row
per named account, validated before anything is recorded, with the administrator's enable decision and the
system's time to live, needing no configuration and not held by a paused one (decision D1). Both share one
Activity shape and coalesce onto each other by person and system.

PendingPasswordChange gains Origin and EnableAccount (migration AddPasswordChangeOrigin; existing rows are
Propagated). The lane claims and expires explicit rows only over an unconfigured or paused system, resolves an
explicit row's account from its own id and parks it when that account is gone, retries under
ConnectedSystemPasswordSynchronisation defaults where no configuration exists, and builds a Connector only once
something is claimed. The due-systems, outlook, summary and header reads treat an explicit row as due wherever it
is; GetChangeOutcomesAsync never reports one as Held.

The open, check, set, classify, close sequence is extracted once into PasswordDeliveryCore and used by the lane,
the initial-password pass (a throwing Connector is now a transient retry there rather than an escaping
exception) and the interim immediate path. ConnectedSystemServer's set-password members stay as shims for the
controller and Razor callers the web layer still compiles against, marked for that layer's rewrite.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The person page's Password Synchronisation and Actions tabs become one
Administrator-gated Password tab: an attention strip for parked or expired
changes with Retry, the Set Password card with the capable-account count,
Still to be delivered with per-row Retry and Stop trying, and Recent
password changes with a Set or Propagated kind chip. The tab's data loads
on first activation of its slug (NavigableMudTabs now raises
ActivePanelSlugChanged); only the badge count is read with the page.

SetPasswordDialog keeps its composition and submits through
PasswordSynchronisationServer.SetPasswordAsync with the ticked accounts as
explicit targets. Its result stage is driven by the outcome waiter: one row
per Connected System reading Set, Retrying (next attempt, Stop trying) or
Parked (the target's words, guidance, Try another password), with the
storage promise reworded per decision D4. The Connected System Object page
uses the same dialog and operation with one target. The Synchronise
Password dialog, the Actions tab and the progress rail are removed.

Application layer, in support: SetPasswordAsync records the change's origin
on the parent Activity's TargetContext and the password history projects it
back as PasswordSynchronisationEvent.Origin (null for older Activities), and
PasswordChangeTargetOutcome carries the queue row's FailureReason so the
dialog can choose remediation guidance.

Service Health cards: the footer's separator dot is drawn in the column gap
and clipped at a line start so a wrap never opens with a dot, and an
unhealthy card that was idle fills its condition slot with its last
heartbeat.

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

REST: POST /api/v1/metaverse/objects/{id}/password is now SetMetaverseObjectPassword, taking
password, optional connectedSystemObjectIds (omitted = every Connected System configured for
Password Synchronisation), expiryBehaviour, enableAccount (named accounts only) and wait (0 to 30,
default 10 with named accounts, 0 when propagating). The response is the per-target outcome shape
plus origin; 200 when settled, 202 when not, 400 for validation and for the core's ArgumentException,
404 for the person. The account-scoped endpoint is a one-target wrapper over the same operation
with the same response, and answers 404 when the object is not joined to a Metaverse Object.

PowerShell: Set-JIMMetaverseObjectPassword gains the propagate default (D5), -Wait and four
parameter sets (Named/Propagate x Supplied/Generated) with -EnableAccount only in the Named sets;
-AllAccounts and Sync-JIMMetaverseObjectPassword are removed. Set-JIMConnectedSystemObjectPassword
returns the same outcome shape, waits by default, gains -Wait and loses -PassThru. A Parked target
is also a non-terminating error carrying the result.

Docs: one operation with two target modes in docs/concepts/passwords.md, D4 wording for what JIM
holds and for how long, the person's Password tab, cmdlet pages and the API overview updated,
Sync- section removed. Changelog amended so 0.15.0 reads as the final shape. Scenario 20 moved to
the new cmdlet. Plan carries a Deviations section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…son on the REST outcome (#1635)

The web and API now call SetPasswordAsync directly, so the ConnectedSystemServer shims, the QueuePasswordChangeAsync forwarders, MultiAccountPasswordSetResult and AccountPasswordSetOutcome go, with the two fixtures that existed only for them; the fan-out tests exercise the propagate mode of the one operation. The per-target failure reason the portal uses for its guidance is now on the REST response and the cmdlet output too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ssword history as a timeline (#1635)

Service Health cards carry the uptime on the heartbeat line and move host,
version and instance behind a per-card details control, each value in
monospace with a copy button; the version-skew chip stays on the card's face
beside the pill and repeats on the Version row. The status pill becomes the
shared .jim-status-pill vocabulary (ok, warn, err, neutral).

The person page's recent password changes become a day-grouped timeline:
one entry per change with the time, kind chip and initiator, a status pill
per Connected System, and words only where a system refused the change or
is still owed it, with Retry and Stop trying on that line. The derivation,
including which change a live queue row belongs to, lives in
PasswordHistoryTimelineModel with its own unit tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant