Task: Update provisioning scripts to set the account's default party
Table of Contents
This page documents a task in the Add a default party per account with a quick-login checkbox story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Give provisioning scripts a way to set the just-provisioned party as
the provisioning account's quick-login default, and use it in both
barclays_system_provision.ores and acme_system_provision.ores so
freshly-provisioned dev environments come up ready for one-click
login.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Add a default party per account with a quick-login checkbox |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-11 |
Acceptance
- A new self-service NATS request (
set_my_default_party_request, subjectiam.v1.accounts.set-default-party) lets a logged-in account set its owndefault_party_id, validated server-side against the account's actual party memberships (mirrorsselect_party's membership check). - A new shell command,
accounts set-default-party <party-uuid-or-full-name>, resolves the party (by UUID or exact full name, same lookup asprovision party) and calls the new request. barclays_system_provision.organdacme_system_provision.org(the recipes, source of truth) callaccounts set-default-partyright afterprovision partysucceeds, while still logged in as the tenant admin.barclays_system_provision.oresandacme_system_provision.oresregenerated viacompass build --direct tangle_shell_scriptsto reflect the recipe change (never hand-edited).
Plan
- Considered doing this transparently inside the existing
provision partycommand vs. adding an explicit new shell subcommand called from the scripts. Chose the explicit subcommand (per direction) — matches the task's literal ask to edit the scripts, and keeps "provision a party" and "make it my default" as two separately-visible, separately-testable steps. - Protocol:
set_my_default_party_request=/=responseinaccount_protocol.hpp, self-service (identity from the JWT, likeupdate_my_email_request, not an admin-on-behalf-of-another-account operation). - Service:
account_service::set_my_default_party(), mirroringupdate_my_email's read-modify-write-via-temporal-versioning shape. - Handler:
account_handler::set_default_party()— extracts the account id from the bearer token, validates the requested party is one of the account's actual memberships (same check asselect_party), then calls the service. Registered inregistrar.cpp. - Shell:
accounts_commands::process_set_default_party()— resolves the party by UUID or exact full name viaget_parties_request(same lookupprovision_commands::process_partyalready uses), then calls the new request. Newaccounts set-default-partysubcommand. - Edited both
.orgrecipes (never the tangled.oresdirectly) and regenerated withcompass build --direct tangle_shell_scripts; confirmed only the two intended.oresfiles changed.
Notes
- Ordering: the tenant-admin-to-party association happens during
provision tenant's phase 3 (associates the admin with every Operational party), which runs beforeprovision partyin both scripts — so by the timeaccounts set-default-partyruns, the membership check server-side always finds the account already associated. account_party_repositorywas already included inaccount_handler.hpp(used bylogin=/=select_party), so no new dependency was needed there.
Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
| Verify default-party quick-login end to end | PASSED | End-to-end across all 3 tasks. |
PRs
| PR | Title |
|---|---|
| #1514 | [ores.iam,ores.qt] Add default party per account with quick-login checkbox |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | Uninitialized defaultPartyId_ spuriously enables Save on Add-Account |
AccountPartiesWidget.hpp |
Accepted | Added = boost::uuids::nil_uuid() default member initializer. |
| 2 | No automated test coverage for the new logic | service_account_service_tests.cpp |
Accepted | Added 4 service-layer tests: set_my_default_party success/idempotent/nonexistent-account, update_account sets-and-clears default_party_id. Declined a handler-level tenant-isolation regression test as impractical for this round (no existing NATS-handler test harness in this file to extend) — noted as a follow-up gap. |
| 3 | set_my_default_party treats "already the default" as an error |
account_service.cpp |
Accepted | Changed to a silent no-op (empty string / success) when the requested party already matches — idempotent re-runs (provisioning scripts, shell command) now succeed instead of hard-failing. |
| 4 | Reverting an account silently wipes its default party | AccountController.cpp (onRevertAccount) |
Accepted | onRevertAccount now threads the historical version's default_party_id into the update_account_request, matching how email is already handled there. |
| 5 | Round 2: edit-mode async race — Save before parties load() resolves resends a nil default-party selection, wiping a real stored default | AccountPartiesWidget.{hpp,cpp}, AccountDetailDialog.cpp |
Accepted | hasPendingDefaultPartyChange() now returns false until defaultPartyComboInitialized_; added isDefaultPartyReady(), gated into updateSaveResetButtonState() (Save stays disabled until parties/default-party data has actually loaded) and defensively re-checked in onSaveClicked(); connected dataLoaded to updateSaveResetButtonState() so the button re-evaluates once data arrives. |
| 6 | Round 3: the round-2 fix reused setDefaultPartyId() to rebase the post-save baseline, which also reset defaultPartyComboInitialized_ to false with nothing guaranteed to set it back (load() only re-runs if party membership changed) — permanently disabling Save for the rest of the dialog session after any default-party-only save |
AccountPartiesWidget.{hpp,cpp}, AccountDetailDialog.cpp |
Accepted | Added a separate rebaseDefaultParty() that updates only the pending-change baseline (defaultPartyId_) without touching defaultPartyComboInitialized_; the post-save success handler now calls this instead of setDefaultPartyId(), which stays reserved for the initial per-account seeding path in setAccount(). |
| 7 | Round 4: the round-3 fix left populateDefaultPartyCombo()'s setCurrentIndex re-seed conditional on !defaultPartyComboInitialized_; a load() re-triggered by a party add/remove alongside a default-party save (defaultPartyComboInitialized_ already true) skipped the re-seed, leaving the combo showing Qt's auto-selected "(none)" while defaultPartyId_ still held the real default — a second Save would then send an empty default_party_id and wipe it |
AccountPartiesWidget.cpp |
Accepted | setCurrentIndex re-seed from defaultPartyId_ is now unconditional on every populateDefaultPartyCombo() call; defaultPartyComboInitialized_ is retained only for gating isDefaultPartyReady()=/=hasPendingDefaultPartyChange(). |
Result
Added a self-service "set my default party" path and wired both provisioning scripts to use it:
- account_protocol.hpp —
set_my_default_party_request=/=response. - account_service.hpp / account_service.cpp —
set_my_default_party(). - account_handler.hpp —
set_default_party()handler, with the same party-membership validationselect_partyuses. - registrar.cpp — subscribes the new handler.
- accounts_commands.hpp / accounts_commands.cpp —
accounts set-default-partyshell command, resolving the party by UUID or full name. - barclays_system_provision.org / acme_system_provision.org — call
accounts set-default-party "<party>"right afterprovision party, regenerated into barclays_system_provision.ores / acme_system_provision.ores viacompass build --direct tangle_shell_scripts.
Verification: rebuilt ores.iam.api.lib, ores.iam.core.lib,
ores.shell.lib clean under linux-clang-debug-make; ran ctest
--preset linux-clang-debug-make -R "iam|shell" — ores.iam.api.tests,
ores.iam.core.tests, ores.iam.service.tests, ores.shell.tests all
green (100% pass, 0 failed). Did not run the provisioning scripts
themselves end-to-end against a live NATS+DB stack (that's a manual
integration check, not covered by ctest) — flagged as a manual QA gap
rather than claimed as covered.