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, subject iam.v1.accounts.set-default-party) lets a logged-in account set its own default_party_id, validated server-side against the account's actual party memberships (mirrors select_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 as provision party) and calls the new request.
  • barclays_system_provision.org and acme_system_provision.org (the recipes, source of truth) call accounts set-default-party right after provision party succeeds, while still logged in as the tenant admin.
  • barclays_system_provision.ores and acme_system_provision.ores regenerated via compass build --direct tangle_shell_scripts to reflect the recipe change (never hand-edited).

Plan

  1. Considered doing this transparently inside the existing provision party command 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.
  2. Protocol: set_my_default_party_request=/=response in account_protocol.hpp, self-service (identity from the JWT, like update_my_email_request, not an admin-on-behalf-of-another-account operation).
  3. Service: account_service::set_my_default_party(), mirroring update_my_email's read-modify-write-via-temporal-versioning shape.
  4. 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 as select_party), then calls the service. Registered in registrar.cpp.
  5. Shell: accounts_commands::process_set_default_party() — resolves the party by UUID or exact full name via get_parties_request (same lookup provision_commands::process_party already uses), then calls the new request. New accounts set-default-party subcommand.
  6. Edited both .org recipes (never the tangled .ores directly) and regenerated with compass build --direct tangle_shell_scripts; confirmed only the two intended .ores files 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 before provision party in both scripts — so by the time accounts set-default-party runs, the membership check server-side always finds the account already associated.
  • account_party_repository was already included in account_handler.hpp (used by login=/=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:

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.

Emacs 29.3 (Org mode 9.6.15)