Customers activate packages via Service Library (tenant app). Each activation is idempotent on (tenant, package_id) — re-activate refreshes non-builtin workflows. Built-in workflows are never overwritten.

Identity-attribute generators

samAccountName generator

pkg.samaccountname

Generates a Windows-compatible sAMAccountName from given_name + family_name, with uniqueness check against existing identities.

  • Triggers on identity.created
  • Writes to identity.attributes.samAccountName
  • Config: max_length (default 20), collision_strategy (suffix-1, suffix-2…)
  • Use case: any tenant using AD as a target

Work email generator

pkg.work_email

Generates a primary work email from given_name + family_name + tenant domain. Handles homonyms (suffix-1, suffix-2).

  • Triggers on identity.created
  • Writes to identity.email (or identity.attributes.work_email)
  • Config: domain, format (first.last, flast, firstlast)
  • Use case: HR-first onboarding without manual email assignment

AD username generator

pkg.ad_username

Generates an AD logon name (typically 8-character with collision-handling). Distinct from samAccountName — covers tenants with separate userPrincipalName conventions.

  • Triggers on identity.created
  • Writes to identity.attributes.ad_username
  • Config: length, style (lowercase only, mixed)
  • Use case: legacy AD environments with strict naming standards

Identity classification

External identity classification

pkg.external_identity_classification

Auto-tags identities as contractor / partner / vendor based on email domain + HR-feed attributes. Drives downstream policy decisions (cert frequency, access-grant scope).

  • Triggers on identity.created + identity.updated
  • Writes to identity.attributes.classification
  • Config: contractor_domain_patterns, partner_attribute_keys
  • Use case: distinguish employees from non-employee identities for time-bound access

Mover-trigger notifications

Manager change notification

pkg.manager_change_notify

When an identity's manager_email changes (mover event), emails the new manager with summary of current access + link to start a smart-cert.

  • Triggers on identity.updated with manager_email diff
  • Action: send_email via NotificationTemplate
  • Config: email_template_id, cert_url_pattern
  • Use case: enforce manager-aware access governance on reorgs

Tenant-feature gates

IVIP visibility mode

pkg.ivip_mode

Switches the tenant into read-only IVIP positioning: provisioning surfaces hidden, only analytics + reconciliation + audit visible.

  • Activates tenant_feature: "ivip-mode"
  • Effects: nav-gating filters 10+ provisioning items, topbar IVIP-badge appears
  • Pair with: TenantPlan.IVIP_VISIBILITY SKU
  • Use case: sovereignty-conscious customers starting read-only before opting into write

Externals & data tab

pkg.externals_data

Unlocks the "Externals & data" nav item — schema-objects management for tenants with custom reference data.

  • Activates tenant_feature: "externals-data"
  • Effects: schema-objects nav becomes visible
  • Use case: tenants importing org-structure / reference-data from non-standard sources

Activation flow

  1. Customer admin opens Service Library in the tenant app
  2. Picks a package card → fills config (each package has a config_schema)
  3. Clicks Activate → backend runs builds(config) → BuildResult
  4. BuildResult writes:
    • CustomProperty rows (target_kind = workflow / tenant_feature)
    • WorkflowDefinition rows (event / scheduled / manual)
  5. Idempotent on (tenant_id, business_id) — re-activate refreshes non-builtin workflows. Built-in workflows are never overwritten.

Adding a new package

  1. Define a PackageSpec in backend/app/domain/workflow_package/registry.py
  2. Append to the _PACKAGES dict
  3. Add config-schema fields (typed via ConfigField)
  4. Write a unit test in tests/test_workflow_packages.py — at minimum a default-build and a config-override build
  5. No DB migration, no seed, no lifespan bootstrap — packages are lazy
Per CLAUDE.md "Workflow Packages" rule: packages should only write to CustomProperty + WorkflowDefinition. Don't expand to NotificationTemplates / IGAGroups in the same package — those have separate seeders for clarity.