Skip to content

feat(directory): Phase 4 — Workflow Designer custom principals + DC wizard wiring#417

Open
larsgeorge-db wants to merge 1 commit into
feat/directory-providersfrom
feat/directory-phase4
Open

feat(directory): Phase 4 — Workflow Designer custom principals + DC wizard wiring#417
larsgeorge-db wants to merge 1 commit into
feat/directory-providersfrom
feat/directory-phase4

Conversation

@larsgeorge-db
Copy link
Copy Markdown
Collaborator

Phase 4 of the directory plan (#375). Stacked on #416 — base is `feat/directory-providers` so the diff is Phase 4 only.

Plan: #375 · Backend (Phase 1): #406 · Phase 1 frontend: #407 · Phase 2: #412 · Phase 3: #413 · Providers: #416

Summary

Workflow Designer — Custom principals toggle

  • Notification step and Approval step config panels each gain a "Custom principals" toggle.
    • Off = today's role-only `Select` (no regression).
    • On = adds a `PrincipalPicker(multi, users+groups)` alongside; picks join the existing `recipients` / `approvers` field as comma-separated literals via the new `joinRoleAndPrincipals` helper.
  • `splitRoleAndPrincipals` is the inverse so the designer hydrates the form losslessly from a persisted config. Role-shaped tokens (`requester`, `owner`, legacy aliases like `domain_owners`, UUID-shaped IDs, `business:`) land in the role slot; everything else falls into the picker side.
  • Toggle state persists on the step config (`recipients_custom` / `approvers_custom`) so the UI re-opens in the same shape.

Data Contract Wizard — wired-up principals

Six fields previously rendered but unwired now flow into both the submit and draft payloads:

Field Picker config Payload key
Contract Owner single, user `owner`
Data Consumers multi, user+group `consumers`
Subject Matter Experts multi, user `subjectMatterExperts`
Read Access groups multi, group `accessControl.readGroups`
Write Access groups multi, group `accessControl.writeGroups`
Primary Support Email single, user `support.primaryEmail`

Backend (small, additive)

`_resolve_role_to_users` now comma-splits unconditionally and recurses on each segment, so the workflow step's "role + custom principals" string resolves each piece in isolation: role tokens still expand to users-in-role; emails and group names pass through as literals; dedup is enforced. The previous `'@'`-gated comma-split is gone; a single email still returns as one literal tuple (regression-tested).

Note: the plan called this a "no backend changes" phase. In practice the existing resolver only handled mixed comma lists when at least one segment contained `@`, which meant role + group + email wouldn't have worked end-to-end. The change here is small (one new branch at the top of the function + flatten + dedup) and strict expansion of behaviour (single-token inputs still resolve identically).

Test plan

  • Backend resolver tests: 12 new in `test_resolve_role_to_users.py` — single-token branches still resolve (`requester` / `owner` / single email / unknown literal); comma-split mixed role+email+group; dedup; whitespace + blank segments; `requester` / `owner` inside a list still expand; legacy single-email path returns one tuple (regression).
  • Backend directory + resolver suite: 79 passed.
  • Frontend join/split tests: 14 new in `workflow-principals.test.ts` — join + split round-trip identity; role-only / principals-only / mixed shapes; UUID / legacy alias / `business:` detection; lossless hydration; extra role-shaped tokens after the first fall into the picker side.
  • Frontend suite: 719 passed, 6 skipped, 0 failed.
  • Type-check clean.
  • Lint clean on touched files (two pre-existing apostrophe errors in `workflow-designer.tsx` lines 1605/2016 are not part of this diff).
  • Manual smoke test in a workspace — toggling the workflow designer custom-principals on/off, exercising the wizard fields end-to-end (left for reviewer / follow-up).

Acceptance criteria from the plan (Phase 4)

  • Notification step toggle reveals picker; picked emails + group names appended to `recipients`; role/literal entries continue to work without the toggle.
  • Approval step toggle works for `approvers` with the same semantics.
  • Workflow execution fan-out unchanged for non-custom principals; mixed lists are resolved segment-by-segment.
  • Wizard owner / stakeholders / groups / primary-support-email values survive the wizard flow and appear on the submitted contract payload (and on `Save Draft`).
  • No regression to the wizard's existing summary screen or to data contracts created via the inline / basic forms.

…izard wiring

Stacks on the Lakebase/File providers (#416). Closes Phase 4 of the
plan #375.

Workflow Designer:
- Notification + Approval step config panels each gain a "Custom
  principals" toggle. Off = today's role-only Select. On = adds a
  PrincipalPicker(multi, users+groups) alongside the role Select; the
  picks are joined into the existing recipients / approvers string
  field as comma-separated literals via the new
  joinRoleAndPrincipals helper. splitRoleAndPrincipals does the
  inverse so the designer hydrates the form from a persisted config
  losslessly (UUID-shaped tokens, ``requester``, ``owner``, legacy
  aliases, and ``business:<uuid>`` are recognised as the role-slot;
  anything else falls into the picker side).
- Toggle state is persisted on the step config (recipients_custom /
  approvers_custom) so the UI re-opens in the same shape.

Backend (small, additive):
- _resolve_role_to_users now comma-splits unconditionally when a
  comma is present and recurses on each segment, so the workflow
  step's "role + custom principals" string resolves each piece in
  isolation: role tokens still expand to users-in-role, emails and
  group names pass through as literals, dedup is enforced. The
  previous '@'-gated comma-split is gone; a single email still
  returns as one literal tuple (covered by tests).

Data Contract Wizard:
- Contract Owner Input -> PrincipalPicker(single, user).
- Data Consumers (multi, user+group), Subject Matter Experts (multi,
  user), Read Access groups (multi, group), Write Access groups
  (multi, group), Primary Support Email (single, user) — all
  previously rendered but unwired. Wired through to both submit and
  draft payloads under ``consumers``, ``subjectMatterExperts``,
  ``accessControl.{readGroups,writeGroups}``, and
  ``support.primaryEmail``.

Tests:
- Backend test_resolve_role_to_users.py (12): single-token branches
  still resolve (requester / owner / email / unknown literal);
  comma-split mixed role+email+group; dedup; whitespace + blank
  segments; requester / owner tokens inside a list still expand;
  legacy single-email path returns one tuple (regression).
- Frontend workflow-principals.test.ts (14): join + split round-trip
  identity; role-only / principals-only / mixed shapes; UUID / alias
  / business: detection; lossless hydration; extra role-shaped
  tokens after the first fall into the picker side.

Totals:
- Backend directory + resolver tests: 79 passed.
- Frontend tests: 719 passed (14 new), 6 skipped, 0 failed.
- Type-check clean; lint clean on touched files (the two pre-existing
  apostrophe errors in workflow-designer.tsx at lines 1605/2016 are
  not in this diff).
@larsgeorge-db larsgeorge-db requested a review from a team as a code owner May 21, 2026 16:44
larsgeorge-db added a commit that referenced this pull request May 21, 2026
…r scope

Documents what shipped under PRs #406 / #407 / #412 / #413 / #416 / #417:

- Renames the integration's manager / routes / settings keys in the
  PRD to match the implementation (Directory layer, /api/directory/*,
  DIRECTORY_* settings, Settings → Directory tab).
- Documents the DirectoryProvider interface and the
  (DirectoryProviderContext, DirectoryProviderConfig) factory
  signature so future provider plug-ins know what to implement.
- Documents the v1 provider set, which expanded during planning
  from Entra-only to entra + lakebase + file. The Lakebase table
  schema and CSV format are included so operators have a single
  reference.
- Preserves story content, the disambiguation rule, both picker
  modes, storage-compatibility guarantees, and graceful-degradation
  rules from the PRD body unchanged.
- Re-confirms the out-of-scope list (Okta/Ping, service principals,
  OBO, profile photos, manager hierarchy, role/team Select replacement,
  CSV bulk import) which the abstraction makes cheap to revisit.
larsgeorge-db added a commit that referenced this pull request May 21, 2026
…r scope

Documents what shipped under PRs #406 / #407 / #412 / #413 / #416 / #417:

- Renames the integration's manager / routes / settings keys in the
  PRD to match the implementation (Directory layer, /api/directory/*,
  DIRECTORY_* settings, Settings → Directory tab).
- Documents the DirectoryProvider interface and the
  (DirectoryProviderContext, DirectoryProviderConfig) factory
  signature so future provider plug-ins know what to implement.
- Documents the v1 provider set, which expanded during planning
  from Entra-only to entra + lakebase + file. The Lakebase table
  schema and CSV format are included so operators have a single
  reference.
- Preserves story content, the disambiguation rule, both picker
  modes, storage-compatibility guarantees, and graceful-degradation
  rules from the PRD body unchanged.
- Re-confirms the out-of-scope list (Okta/Ping, service principals,
  OBO, profile photos, manager hierarchy, role/team Select replacement,
  CSV bulk import) which the abstraction makes cheap to revisit.
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