Skip to main content

Qontak | Platform | One Team Migration — Phase 1: Legacy Team Migration

Product Requirements Document · NEW PRD v1.0


HEADER BLOCK

FieldValue
PMQontak PM Group
PRD Version1.0
StatusDRAFT
PRD TypeNEW
EpicBIF-8642
SquadBifrost
RFC LinkTBD — pending PRD approval
Figma MasterTBD — pending design
AnchorYes — Qontak | Platform | One Team Migration — ANCHOR
Labelsepic:qontak-platform | module:usman | feature:one-team-migration
Last Updated2026-06-22

✅ Reformat Complete

This PRD was reformatted from [PRD] Qontak One Team Migration. All gaps resolved as of 2026-06-23. No outstanding flags.


Table of Contents


3. One-liner + Problem

One-liner: Migrate existing Chat Divisions and CRM Teams into a unified Launchpad USMAN system for Qontak One Admins, with mandatory team assignment enforced for all new user invitations.

Problem: Chat and CRM currently handle team structures independently — Chat uses "Divisions" and CRM uses "Teams" — with no unified view in Qontak One. This fragmentation forces Admins to manage user memberships across multiple disconnected places, allows new users to be invited without team assignments (creating untracked access), and leaves no common source of truth for team origin. As Qontak One centralizes User Management into Launchpad, all existing team structures must be migrated, clearly labeled by source, and new user invitations must require team assignment to maintain organizational structure going forward.


4. Target Users + Persona Context

PersonaRoleGoalPainWorkaround
Primary — AdminCompany admin managing users in Qontak OneInvite new users with proper team categorization; view all team structures in a single Launchpad panelTeams from Chat (Divisions) and CRM are disconnected — no single place to manage all team structuresManages teams separately in Chat Divisions settings and CRM Teams settings; no cross-system view
Secondary — Backend DeveloperEngineer triggering & operating the migration (Launchpad triggers; Chat/CRM push)Safely migrate legacy team structures without disrupting existing user configurationsNo source_identifier or reference_id on existing teams — cannot trace which system a team came fromManual lookup across Chat and CRM databases to identify team origins

5. Non-Goals

  1. This does not migrate individual user memberships — only team structures (Divisions/Teams) are migrated, not user-to-team assignments.
  2. This does not modify the existing Chat Divisions UI — Chat Divisions continue to function as-is.
  3. This does not merge duplicate team names across Chat and CRM — each is migrated as a separate Launchpad team with its source prefix.
  4. This does not migrate inactive or archived Divisions/Teams — active records only.
  5. This does not enforce team assignment for existing users already in the system — only new invitations.
  6. The Chat and CRM SUPPORT squad PRDs (API endpoints to provide Division/Team data) are out of scope for this PRD.
  7. This does not change the billing or quota model for teams.

Scope Changes

  • Backend — new source_identifier and reference_id DB columns on Team table; migration script; invitation validation; General team auto-creation on CID creation.
  • Frontend — mandatory Team dropdown on /user/invite; Qontak One Team Sidebar URL change from /qontakcrm/team to /launchpad/team.

6. Constraints

Feature Flags:

  • unified_app | default: false — gates Qontak One globally (existing flag). Team migration features only active when unified_app = true.
  • one_team_migration_enabled | default: false — gates this migration rollout (new flag, global). Mandatory team invitation and migrated team data only visible when this flag is enabled.

Plan Scope: Available to all plans with unified_app = true. No additional plan restriction.

Platform: Web only. No mobile app scope.

Performance SLAs:

  • Migration script: processes all active CIDs within 24 hours of execution
  • Invitation team validation API response: ≤ 500ms
  • General team auto-creation on CID creation: ≤ 2s

Data Limits: To be defined by engineering — see Section 17 (Open Questions).

6.1 Data Lifecycle

The migration is a one-time, idempotent operation — each source squad pushes its mapped teams into Launchpad's create endpoint. The only artifacts produced outside the main data model are migration failure logs (the bulk upload-job records and per-item results).

Artifact TypeRetention PeriodCleanup TriggerUser-Visible Effect
Migration failure log30 daysNightly cleanup jobNone — engineer-visible in logs only
Migration run recordIndefiniteManual cleanup by engineeringNone

7. Feature Changes

CHG-001 — Team Sidebar URL Change

FieldValue
Change TypeModified routing
Page (Before)/qontakcrm/team
Page (After)/launchpad/team
Page IntentTeam management for Admin — view, create, and manage Launchpad teams
ElementBeforeAfter
Team sidebar URL/qontakcrm/team/launchpad/team
Navigation link targetCRM moduleLaunchpad module
Old URL behaviorActiveRedirects to /launchpad/team

Figma: TBD — pending design


8. New Features

Feature: Mandatory Team Dropdown on User Invitation Form

FieldValue
URL/user/invite
AccessAdmin only

Component Tree:

InviteUserForm
├── EmailInput (existing)
├── RoleDropdown (existing)
├── TeamDropdown ★ NEW (required field — marked with *)
│ └── TeamOption (one per active Launchpad team for this CID)
└── SubmitButton (existing — disabled until all required fields valid)

UI States:

  • Loading: Skeleton/spinner in TeamDropdown while teams are fetching from API
  • Empty: "No teams available. Create a team first." — SubmitButton disabled
  • Error: "Could not load teams. Try again." + Retry button — SubmitButton disabled until retry succeeds
  • Success: TeamDropdown populated with active Launchpad teams; SubmitButton enabled when team selected

Figma: TBD — pending design


9. API & Webhook Behavior

#BehaviorEntity AffectedTriggered ByExpected BehaviorFailure Behavior
1Create Launchpad Team (migration)Launchpad Team (new record created)Chat or CRM squad calls Launchpad's bulk/single team-create endpoint with is_migrate = true, app = chat|crm, and the source app_identifier_id (= division/team id) (one_team_migration_enabled = true)Launchpad persists the team with prefixed name (Chat - [Name] / CRM - [Name]), derives source_identifier from app, stores app_identifier_id as reference_id; idempotent — skips if a team with the same (company, source_identifier, reference_id) already exists; emits a TEAM_MIGRATED Kafka event carrying team_id + app + app_identifier_id (so the source squad can map its division/team to the centralized team)Records per-item failure with CID + reason in the bulk result; batch continues without aborting
2Validate team on user invitationUser invitation payloadAdmin submits Invite User form at /user/inviteRejects submission with validation error if no team selected (per S8 §TeamDropdown required); bypasses validation for existing legacy users without a teamReturns validation error: "Please select a team to continue"
3Auto-create General team on CID creationLaunchpad Team (new record created)New CID successfully created in systemCreates team named "General" with: no members, no parent team, source_identifier = 'Launchpad', reference_id = nullCID creation NOT rolled back; failure logged; team creation retried asynchronously
4Fetch teams for invitation dropdownLaunchpad Team list (read)Invite User form loads at /user/inviteReturns list of active Launchpad teams for the CIDReturns error; frontend shows "Could not load teams. Try again." + retry button
5Trigger source-squad migrationChat Division / CRM Team (owned & read by their squads)Migration kickoff (one_team_migration_enabled = true)Launchpad triggers Chat and CRM migration via their endpoints; each squad maps its own active Divisions/Teams and calls Launchpad's bulk/single create (behavior #1)If a squad's trigger or processing fails, that squad retries/skips on its side; Launchpad is not blocked and records per-item create results

[Implementation details — HTTP method, path, request/response schema, error codes: to be resolved by Claude during RFC. Migration ownership: each source squad performs its own data mapping and pushes into Launchpad's create endpoints; Launchpad does not read Chat/CRM data directly.]


10. System Flow + User Stories + ACs

10.1 System Flow

Flow: Migration Script + New User Invitation + CID Creation

flowchart TD
subgraph Migration["Migration (push model)"]
A["Launchpad triggers Chat & CRM migration"] --> B{Which squad?}
B -->|Chat| C["Chat maps its active Divisions<br/>then calls Launchpad bulk/single create<br/>(is_migrate, app=chat, reference_id)"]
B -->|CRM| D["CRM maps its active Teams<br/>then calls Launchpad bulk/single create<br/>(is_migrate, app=crm, reference_id)"]
C --> G["Launchpad persists team:<br/>name 'Chat - [Name]', source_identifier='Chat', reference_id=app_identifier_id"]
D --> H["Launchpad persists team:<br/>name 'CRM - [Name]', source_identifier='CRM', reference_id=app_identifier_id"]
G --> K["Emit TEAM_MIGRATED event<br/>(team_id, app, app_identifier_id)"]
H --> K
K --> M["Chat/CRM consume event<br/>map division/team to Launchpad team"]
end

subgraph New User Invitation
I["Admin invites new user via /user/invite"] --> J{Is Team Selected?}
J -->|No| K[Return Validation Error]
J -->|Yes| L["Set source_identifier = 'Launchpad'"]
L --> M[User created and bound to Team]
end

subgraph CID Creation
N[New CID Created] --> O["Auto-create 'General' Team"]
O --> P["source_identifier = 'Launchpad', reference_id = null"]
end

10.2 User Stories

User StoryImportanceMockup / Technical NotesAcceptance Criteria
[OTM-S01] — Mandatory Team Selection for New Users

As an Admin, I want the system to require a Team selection when inviting a new user, so that all new users in Qontak One are properly categorized and tracked.
Must HaveFigma: TBD — pending design

Data Fields:
team_id (string, required) — selected from TeamDropdown
source_identifier (enum, system-set) — always 'Launchpad' for new invitations

Before-After Behavior: Before: Admin can submit /user/invite without selecting a team. After: TeamDropdown is required (*); submission blocked with validation error if no team selected.
— Happy Path —
• AC-1: Given an Admin is on /user/invite AND one_team_migration_enabled = true, when the page loads, then the TeamDropdown is visible and marked required (*).
• AC-2: Given the TeamDropdown is visible and a team is selected, when the Admin submits the form with all required fields valid, then the user is invited successfully with source_identifier = 'Launchpad' and the selected team assignment.

— Error / Unhappy Path —
• ERR-1: Given the Admin submits the form without selecting a team, when the form validates, then the submission is blocked and inline error shows: "Please select a team to continue."
• ERR-2: Given the team list API fails to load, when the Admin opens /user/invite, then TeamDropdown shows "Could not load teams. Try again." with a retry button, and SubmitButton is disabled until teams load successfully.

— Permission Model —
• CAN: Admin role
• CANNOT: Regular users, read-only roles
• Unauthorized: TeamDropdown not rendered; no validation error exposed

— UI States —
• Loading: Skeleton/spinner in TeamDropdown while teams fetching
• Empty: "No teams available. Create a team first." — SubmitButton disabled
• Error: "Could not load teams. Try again." + retry — SubmitButton disabled
• Success: TeamDropdown populated; SubmitButton enabled when team selected

— Negative Scenarios —
• NEG-1: Given an existing user without a team assignment, when their profile is accessed or updated, then no team assignment is forced and no error is shown.
[OTM-S02] — Auto-Create "General" Team on CID Creation

As Launchpad, I want to automatically create a "General" team when a new CID is created, so that new users can be immediately assigned to a team and CRM Layout activates.
Must HaveFigma: N/A — backend only, no new UI

Data Fields:
team_name (string, system-set) — always "General"
source_identifier (enum, system-set) — always 'Launchpad'
reference_id (null, system-set) — always null for native Launchpad teams
members (empty array) — no initial members
parent_team (null) — no parent

Before-After Behavior: Before: No default team is created when a new CID is created. After: A "General" team with source_identifier = 'Launchpad' is auto-created on every new CID creation event.
— Happy Path —
• AC-1: Given a new CID is successfully created, when the CID creation event fires, then a Launchpad team named "General" is created with: no members, no parent team, source_identifier = 'Launchpad', reference_id = null.
• AC-2: Given the "General" team is created, when an Admin opens /user/invite for that CID, then "General" appears in the TeamDropdown.

— Error / Unhappy Path —
• ERR-1: Given the General team creation service is unavailable at CID creation time, when the creation event fires, then the CID creation is NOT rolled back; the failure is logged; team creation is retried asynchronously.

— Permission Model —
• CAN: System (triggered automatically on CID creation — no user action)
• CANNOT: End users cannot trigger or prevent this
• Unauthorized: N/A — system-triggered only

— UI States —
• N/A — backend only
[OTM-S03] — Qontak One Team Sidebar URL Change

As a user, I want the Team management section to be accessible at /launchpad/team, so that team management is unified under Launchpad navigation in Qontak One.
Must HaveFigma: TBD — pending design

Data Fields: N/A — routing change only

Before-After Behavior: Before: Team management accessible at /qontakcrm/team. After: Team management accessible at /launchpad/team; old URL redirects to new URL.
— Happy Path —
• AC-1: Given a user navigates to /launchpad/team AND unified_app = true AND one_team_migration_enabled = true, when the page loads, then the Launchpad Team management page renders.
• AC-2: Given a user navigates to the old URL /qontakcrm/team, when the page loads, then the user is redirected to /launchpad/team.
• AC-3: Given the Qontak One sidebar is visible, when the Teams navigation link is clicked, then it navigates to /launchpad/team.

— Permission Model —
• CAN: Admin and users with team management permission
• CANNOT: Users without team management permission
• Unauthorized: Redirect to dashboard

— UI States —
• Loading: Skeleton while team list loads at /launchpad/team
• Empty: "No teams yet."
• Error: "Could not load teams. Try again."
• Success: Team list rendered
[OTM-S04] — Track Team Source Identifiers

As a Backend Developer, I want source_identifier and reference_id columns on the Team database table, so that we can trace whether a team was migrated from Chat, CRM, or created natively in Launchpad.
Must HaveFigma: N/A — backend DB schema change only

Data Fields:
source_identifier (enum, required) — values: 'Chat', 'CRM', 'Launchpad'
reference_id (string/int, nullable) — Chat division_id, CRM team_id, or null for native Launchpad teams

Before-After Behavior: Before: Team table has no source tracking columns. After: All Team records carry source_identifier (enum: Chat/CRM/Launchpad) and nullable reference_id linking back to the source system.
— Happy Path —
• AC-1: Given a team is created natively via Launchpad UI, when the record is saved, then source_identifier = 'Launchpad' and reference_id = null.
• AC-2: Given a team is migrated from Chat Divisions, when the migration script creates the record, then source_identifier = 'Chat' and reference_id = [division_id from Chat].
• AC-3: Given a team is migrated from CRM Teams, when the migration script creates the record, then source_identifier = 'CRM' and reference_id = [team_id from CRM].
• AC-4: Given any Team record, when source_identifier is written, then only 'Chat', 'CRM', or 'Launchpad' are accepted — any other value is rejected by DB constraint.

— Permission Model —
• CAN: Migration script (system), auto-creation (system)
• CANNOT: End users cannot modify source_identifier or reference_id
• Unauthorized: N/A — backend columns, not user-editable

— UI States —
• N/A — backend DB schema
[OTM-S05] — Process Pushed Legacy-Team Migration

As a Backend Developer, I want Launchpad to trigger each source squad's migration and idempotently process the teams they push (carrying source + reference_id), emitting a team event per creation, so that legacy team structures are centralized without duplicates — while each squad owns the mapping of its own data.
Must HaveFigma: N/A — backend, no new UI

Ownership: Chat and CRM each map their own active Divisions/Teams and call Launchpad's bulk/single create endpoint (is_migrate = true, app = chat|crm, app_identifier_id), then consume the TEAM_MIGRATED event to map their division/team to the centralized Launchpad team. Launchpad does not read Chat/CRM data.

Data Fields (what the source squad sends → what Launchpad persists):
• Chat: app='chat', app_identifier_id=[division_id]team_name = 'Chat - [Division Name]', source_identifier = 'Chat', reference_id = [division_id]
• CRM: app='crm', app_identifier_id=[team_id]team_name = 'CRM - [Team Name]', source_identifier = 'CRM', reference_id = [team_id]

Before-After Behavior: Before: Chat Divisions and CRM Teams exist only in their respective systems. After: each squad pushes its active records into Launchpad, which stores them as source-tracked teams.
— Happy Path —
• AC-1: Given Chat calls Launchpad's create endpoint for a Division named "Support" (is_migrate, app=chat, app_identifier_id=division_id), when Launchpad processes it, then a team "Chat - Support" exists with source_identifier = 'Chat' and reference_id = [division_id], and a TEAM_MIGRATED event is emitted carrying team_id + app + app_identifier_id.
• AC-2: Given CRM calls the create endpoint for a Team named "Sales" (app=crm, app_identifier_id=team_id), when Launchpad processes it, then a team "CRM - Sales" exists with source_identifier = 'CRM' and reference_id = [team_id], and TEAM_MIGRATED carries app_identifier_id = [team_id].
• AC-3: Given a team with the same (company, source_identifier, reference_id) already exists, when the same create call is replayed, then no duplicate is created (idempotency).
• AC-4: Given a source squad maps its records, then only ACTIVE Divisions/Teams are submitted to Launchpad — the active-only filter is owned by the source squad.

— Error / Unhappy Path —
• ERR-1: Given a source squad's migration is unavailable or fails for a CID, when migration runs, then Launchpad is not blocked; the squad retries/skips on its side and Launchpad records per-item create results in the bulk job.

— Permission Model —
• CAN: Chat/CRM squad services (call the create endpoint); Launchpad (trigger + process)
• CANNOT: End users cannot trigger migration
• Unauthorized: service-to-service auth between squads and Launchpad

11. Rollout

Feature Flags:

  • unified_app | default: false (existing) — Qontak One global gate
  • one_team_migration_enabled | default: false (new, global) — migration rollout gate

Rollout Sequence:

PhaseAudienceGate
Phase 1 — Internal QAQontak internal accounts onlyone_team_migration_enabled = true for internal CIDs
Phase 2 — Staged RolloutCID batches (after CRM squad migration completes)Enable one_team_migration_enabled per CID batch
Phase 3 — GAAll CIDs with unified_app = trueAll staged rollout gates sustained

Backward Compatibility: Existing users without a team remain unaffected — no retroactive team assignment enforced.

Migration: One-time script, idempotent — safe to re-run without creating duplicates.

Dependency: Phase 2 cannot begin until CRM squad's migration is complete (see Section 15 — Dependencies).

11.1 Migration Transition Window

During staged rollout, CIDs will be in different states simultaneously:

CID StateBehavior
one_team_migration_enabled = falseRetains existing behavior; invitation form shows no mandatory team dropdown; /qontakcrm/team still accessible
one_team_migration_enabled = true, migration not yet runInvitation form shows mandatory TeamDropdown; if no teams exist yet, dropdown shows "No teams available. Create a team first."
one_team_migration_enabled = true, migration completeInvitation form shows mandatory TeamDropdown; migrated teams (Chat/CRM prefixed) and any native Launchpad teams appear

End state: All CIDs have one_team_migration_enabled = true and migration complete — transition fallback behavior no longer needed.


12. Observability

Key Events:

Event NameTriggerProperties
team_migrated (Kafka TEAM_MIGRATED)Launchpad persists a pushed migrated teamcid, team_id, source_identifier, app , app_identifier_id (= reference_id), team_name — consumed by Chat/CRM to map their division/team to the centralized team
team_migration_failedScript fails to migrate a teamcid, source, reason
user_invited_with_teamAdmin invites a user with team selectedcid, team_id, source_identifier
invitation_rejected_no_teamInvitation rejected due to missing team selectioncid, admin_id
general_team_createdGeneral team auto-created on CID creationcid, team_id
FieldDetail
Dashboard ownerBifrost PM + Engineering lead
AlertsAlert 1: team_migration_failed rate > 1% in any migration run → #bifrost-alerts. Alert 2: general_team_created failure > 0 in any 24-hour window → #bifrost-alerts.

12.1 Post-Launch Monitoring Cadence

Review cadence: Weekly for first 4 weeks post-GA, then monthly. Owner: Bifrost PM + Engineering lead.

Trigger thresholds:

  • team_migration_failed rate > 1% of total migrations attempted → immediate investigation
  • invitation_rejected_no_team spikes unexpectedly post-GA → PM review within 48 hours
  • general_team_created failures exceed 0 in any 24-hour window → engineering escalation same day

13. Success Metrics

Adoption & Migration:

Primary KPI: % of active CIDs with one_team_migration_enabled = true that have all legacy teams successfully migrated to Launchpad

  • Baseline: 0% (pre-migration)
  • Target: 100% within 30 days of GA

Quality:

  • Migration failure rate (team_migration_failed / total teams attempted)
    • Baseline: N/A (new process)
    • Target: ≤ 1%

Enforcement:

  • % of new user invitations that include a team assignment (for CIDs with one_team_migration_enabled = true)
    • Baseline: N/A (currently unenforced)
    • Target: 100% (validation enforced for all enabled CIDs)

14. Launch Plan & Stage Gates

StageAudienceDurationSuccess GateOwner
Internal QAQontak internal accounts1 weekMigration script completes with 0 duplicates; source_identifier + reference_id correct on all records; invitation validation working; 0 P0/P1 bugsPM + QA
Staged RolloutSubset of CIDs (after CRM squad migration complete)2 weeksMigration failure rate ≤ 1%; mandatory team dropdown working on /user/invite; General team auto-created on new CIDs; old URL /qontakcrm/team redirects to /launchpad/teamPM + Eng lead
GAAll CIDs with unified_app = trueOngoingAll staged rollout gates sustained for 1 consecutive week; one_team_migration_enabled enabled globallyPM

15. Dependencies

DependencyOwning TeamDeliverable NeededBlocking?
Chat squad migrationChat SquadChat maps its own active Divisions and calls Launchpad's bulk/single create (is_migrate, app=chat, reference_id); exposes a migration-trigger endpoint Launchpad can callYES
CRM squad migration (mapping)CRM SquadCRM maps its own active Teams and calls Launchpad's create endpoint (app=crm, reference_id); exposes a migration-trigger endpointYES
CRM squad migration (completion)CRM SquadCRM Teams migration must complete before Phase 2 staged rollout beginsYES

16. Key Decisions + Alternatives Rejected

16a — Decisions Made

DateDecisionRationale
2026-06-23source_identifier uses a fixed enum (Chat, CRM, Launchpad) — not free textType-safe, queryable, consistent across migration and invitation flows
2026-06-23reference_id stores the original Chat Division or CRM Team record IDEnables reverse lookup to legacy record without a cross-DB join
2026-06-23Name conflict resolution: prefix with Chat- / CRM- — no mergeMerging would conflate users from different team contexts; confirmed via ANCHOR OQ1
2026-06-23General team auto-created on new CID creation — not manually set upEnsures all new CIDs have at least one team available for mandatory invitation enforcement
2026-06-23No rollback path — migration failures fixed forwardA rollback mechanism adds complexity with no clear safe-state to return to; confirmed via ANCHOR OQ3
2026-06-30Migration is push, not pull — each source squad maps its own data and calls Launchpad's existing bulk/single team-create endpoint; Launchpad does not read Chat/CRM dataSource data ownership stays with each squad; reuses Launchpad's existing is_migrate/app bulk-create path; avoids Launchpad coupling to Chat/CRM internal schemas

16b — Alternatives Rejected

AlternativeWhy RejectedDate
Free-text source fieldNot type-safe and difficult to query consistently across migration and audit tooling2026-06-23
Merge conflict teams into one recordMerging teams from different product contexts risks mixing users who belonged to separate workflows2026-06-23

17. Open Questions

#TypeQuestionOwnerDeadline
OQ1TECHNICALWhat is the data volume limit per migration batch run — how many teams can be processed per CID per execution? (noted as TBD in S6 Data Limits)Bifrost EngineeringBefore Phase 1 RFC
OQ2PRODUCTUsers currently assigned to both a Chat Division and a CRM Team — do they get assigned to both migrated teams in USMAN, or only one?Bifrost PM + Chat/CRM squadsBefore Phase 1 RFC
OQ3TECHNICALIs migration triggered automatically when one_team_migration_enabled is toggled on, or does Engineering run it as a one-time script per CID?Bifrost EngineeringBefore Phase 1 RFC

PRD CHANGELOG

VersionDateBySectionTypeSummary
1.02026-06-22ClaudeAllREFORMATTEDReformatted from [PRD] Qontak One Team Migration to Qontak PRD template v1.1. 2 sections extracted from source (S1, S8 flow), 13 sections filled via coaching interview, 2 flags remaining (S16 Key Decisions, S17 Open Questions).
1.12026-06-23ClaudeS12, S16, S17, Flag SummaryFILLEDAdded S12 dashboard owner + alert routing (#bifrost-alerts). Filled S16 Key Decisions (5 decisions, 2 alternatives rejected). Added S17 Open Questions (3 questions: migration batch limit, dual-team user assignment, migration trigger mechanism). Flag summary updated to ✅ Reformat Complete.
1.22026-06-30ClaudeS9, S10 (flow + OTM-S05), S15, S16REVISEDMigration model corrected from pull to push to match service planning: each source squad maps its own data and calls Launchpad's existing bulk/single team-create endpoint (is_migrate/app/reference_id); Launchpad triggers them and processes creation + emits Kafka events. Reworked §9 behaviors #1/#5, §10.1 flow diagram, OTM-S05 story + ACs, §15 dependencies, +§16 decision.
1.32026-06-30ClaudeS9, S10.1, S10 OTM-S05, S12REVISEDMigration-mapping refinement: bulk/single create accepts optional app_identifier_id (= source division/team id, persisted as reference_id); migrate-mode emits a separate TEAM_MIGRATED Kafka event (vs TEAM_CREATED) carrying team_id+app+app_identifier_id so each source squad maps its division/team to the centralized Launchpad team. Updated §9 #1, §10.1 flow, OTM-S05 ownership/ACs, §12 event.