Skip to content

fix(provisioning): a Management API consumer's first run is one file, one apply - #237

Merged
windischb merged 1 commit into
developfrom
fix/planner-consumer-findings
Sep 18, 2026
Merged

windischb merged 1 commit into
developfrom
fix/planner-consumer-findings

Conversation

@windischb

Copy link
Copy Markdown
Contributor

Summary

Reported from the field by a Management API consumer (amZettel) with the standard first manifest: a role on the system app, a service account with a credential, and a group binding the two — handles throughout. Three things stood in the way. None of them was the apply; two were the plan lying and one was the section order.

# Symptom Cause Fix
1 "#mgmt-sa" in a group's Members refused up front as Manifest.HandleAppliedTooLate Service accounts applied after groups. A credential only references apps and scopes, both applied earlier. Service accounts now apply before groups, like users. The plan already listed sections in that order; applier and order check agree now.
2 Re-planning an export announced Member 'x' — this realm has no such entity on an unchanged group; the id-only spelling of the same member read as an update The planner canonicalized and resolved Members against users only, while the exporter writes a service-account member as { Key, Id } like any other Members resolve against users, nested groups and service accounts — the three kinds the applier's member resolver has accepted since the always-staged wave
3 A role on modgud announced as the apply SKIPS this role entirely. Import the app first — while the apply created it fine The planner took its app catalog off the export, which leaves the system app out on purpose; the applier's resolver is seeded from the live query The plan reads the realm's apps live, as the apply does. Same fix covers a client, API or scope pointing at the system app.

The Management API permissions (oauth-authorization:*, app-scope:read, position:read, …) live on the system app, so #3 hits every consumer of the surface #236 added.

Not changed

  • resource= on the device-authorization endpoint is rejected with invalid_target although RFC 8707 §2 allows it there. A separate OAuth item, not the planner.
  • The ClientSecret is ignored for an existing client plan note stays: it is the honest signal that the file carries dead weight.

Test plan

  • A_management_api_consumer_provisions_role_service_account_and_group_in_one_run — plan without a manifest error or a phantom note, apply lands role + account + group + membership in one run, export re-plans as unchanged and note-free, id-only member spelling is the same membership. The field report is the red run on the released build.
  • Existing order test (A_handle_that_cannot_honour_its_promise_is_refused_or_reported) still green — positions still apply after groups.
  • Full backend suite green (835 integration + 1668 unit)
  • CI on this PR

🤖 Generated with Claude Code

… one apply

Reported from the field (amZettel, 2026-09-18) with the standard first manifest of a
Management API consumer: a role on the system app, a service account with a credential,
and a group binding the two — handles throughout. Three things stood in the way, and
none of them was the apply:

1. Service accounts applied AFTER groups, so "#mgmt-sa" in a group's Members was refused
   up front as Manifest.HandleAppliedTooLate. A credential only references apps and
   scopes, both applied earlier, so the account moves before groups — like users. The
   plan already listed the sections in that order; applier and order check now agree.

2. The planner canonicalized and resolved group Members against USERS only. The exporter
   writes a service-account member as { Key, Id } like any other, so re-planning an export
   announced "Member 'x' — this realm has no such entity" on an unchanged group, and the
   id-only spelling of the same member read as an update. Members now resolve against
   users, nested groups and service accounts — the three kinds the applier's member
   resolver has accepted since the always-staged wave.

3. The planner took its app catalog off the EXPORT, which leaves the system app out on
   purpose (seeded, not authored). A role on "modgud" — where oauth-authorization:* and
   the other Management API permissions live — was therefore announced as "the apply
   SKIPS this role entirely. Import the app first", while the apply created it fine: its
   app resolver is seeded from the live query. The plan now reads the realm's apps live,
   as the apply does; the same fix covers a client, API or scope pointing at the system
   app.

Test: A_management_api_consumer_provisions_role_service_account_and_group_in_one_run —
plan without a manifest error or a phantom note, apply lands role + account + group +
membership in one run, export re-plans as unchanged and note-free, and the id-only member
spelling is the same membership. The field report is the red run on the released build.

Docs: the reference paragraph in realm-provisioning names all three member kinds and the
apply order; the Members field description and the identity check's comment no longer
claim service accounts apply after groups.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@windischb
windischb merged commit c6380e5 into develop Sep 18, 2026
8 checks passed
@windischb
windischb deleted the fix/planner-consumer-findings branch September 18, 2026 19:49
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