# Create Insights User Skill

This skill automates the creation of Insights users with artist-level (LabelParticipant) permissions using the Altafonte user creation step function. **Supports bulk user creation** for efficient processing of multiple users in a single request.

## Overview

The skill performs the following steps:
1. Query Neo4j prod to get all LabelParticipant UUIDs for the specified GlobalParticipant ID(s) (once per artist, not per user)
2. Generate two CSV files with ALL users:
   - No-email version: One row per user per LabelParticipant with `SendEmail=N`
   - Send-email version: One row per user with `SendEmail=Y`
3. Upload the no-email CSV to S3 (triggers step function)
4. Monitor the step function execution
5. Upload the send-email CSV to S3 (triggers email invites)
6. Monitor the second step function execution
7. Query Neo4j for Identity IDs for ALL users
8. Generate Harness segment CSV (no header) with ALL identity IDs

## Parameters

- `users` (required): JSON array of user objects. Each object must have `first_name`, `last_name`, and `email`.
  - Single user example: `[{"first_name": "Dylan", "last_name": "Bourne", "email": "dylan@example.com"}]`
  - Multiple users example: `[{"first_name": "Dylan", "last_name": "Bourne", "email": "dylan@example.com"}, {"first_name": "Alice", "last_name": "Smith", "email": "alice@example.com"}]`
- `artist_urls` (required): Insights artist page URL(s) or GlobalParticipant ID(s). Can be comma-separated for multiple artists. All users will be granted access to ALL specified artists.
  - Example URL: `https://insights.sonymusic.com/artist/f804f611-da3c-4b60-aa6e-ddfe39e6aeba/`
  - Example ID: `f804f611-da3c-4b60-aa6e-ddfe39e6aeba`
  - Multiple artists: `f804f611-da3c-4b60-aa6e-ddfe39e6aeba,a1b2c3d4-e5f6-7890-abcd-ef1234567890`
- `output_name` (optional): Base name for output CSV files. For bulk requests, use a descriptive name like `Insights-Insanity-Records-2026-04-16`. If not provided, will auto-generate from first user's name.
- `skip_email` (optional): If true, skip the send-email step (default: false)

## Usage Examples

### Single User
```
/create-insights-user \
  --users '[{"first_name": "Dylan", "last_name": "Bourne", "email": "dylan@bournecreatives.com"}]' \
  --artist_urls "https://insights.sonymusic.com/artist/f804f611-da3c-4b60-aa6e-ddfe39e6aeba/"
```

### Multiple Users (Bulk)
```
/create-insights-user \
  --users '[{"first_name": "Andy", "last_name": "Varley", "email": "av@insanity.com"}, {"first_name": "Alice", "last_name": "Beal", "email": "ab@insanity.com"}, {"first_name": "Kieran", "last_name": "Cullen", "email": "kc@insanity.com"}]' \
  --artist_urls "f804f611-da3c-4b60-aa6e-ddfe39e6aeba,a1b2c3d4-e5f6-7890-1234-567890abcdef" \
  --output_name "Insights-Insanity-Records-2026-04-16"
```

### Create Without Sending Emails
```
/create-insights-user \
  --users '[{"first_name": "Dylan", "last_name": "Bourne", "email": "dylan@bournecreatives.com"}]' \
  --artist_urls "f804f611-da3c-4b60-aa6e-ddfe39e6aeba" \
  --skip_email true
```

## AWS CLI Wrapper Requirement

This skill requires the `aws-with-awsume` wrapper script located at `~/.local/bin/aws-with-awsume`. This wrapper:
- Automatically sources awsume credentials for the specified AWS profile
- Suppresses terminal escape sequences and noise
- Runs AWS CLI commands with proper temporary credentials

All AWS CLI commands in this skill should be prefixed with `aws-with-awsume prod` instead of just `aws`.

**Example:**
```bash
# Instead of:
aws s3 ls

# Use:
aws-with-awsume prod s3 ls
```

## Implementation Steps

### Step 1: Parse Users Array

Parse the `users` JSON parameter into an array of user objects. Validate each has `first_name`, `last_name`, and `email`.

### Step 2: Extract GlobalParticipant IDs

Parse the `artist_urls` parameter to extract GlobalParticipant UUID(s):
- If it's a URL like `https://insights.sonymusic.com/artist/UUID/`, extract the UUID
- If it's already a UUID, use it as-is
- Split by comma and process each one
- Remove duplicates

### Step 3: Query Neo4j for LabelParticipant UUIDs

For each **unique** GlobalParticipant ID, run this Cypher query using the `mcp__neo4j-prod__prod-read_neo4j_cypher` tool:

```cypher
MATCH (gp:GlobalParticipant {id: "<global_participant_id>"})-[:REPRESENTS]->(lp:LabelParticipant)
RETURN lp.uuid
```

Collect all LabelParticipant UUIDs across all artists. These will be shared across all users.

### Step 4: Generate CSV Files

Determine the filename:
- If `output_name` is provided, use it
- Otherwise, generate: `Insights-<FirstUser.FirstName>-<FirstUser.LastName>-<YYYY-MM-DD>`

**CSV Headers:**
```
FirstName,LastName,Email,TenantUUID,TenantType,IsMasterContact,IsAdmin,Catalog,Marketing,Analytics,Accounting,Collaborators,Banking,SendEmail
```

**No-Email CSV:**
For each user, create a row for EACH LabelParticipant UUID:
```
<first_name>,<last_name>,<email>,<lp_uuid>,label_participant,N,N,N,N,Y,N,N,N,N
```

Example with 2 users and 3 LabelParticipants = 6 rows total:
```
Dylan,Bourne,dylan@example.com,uuid1,label_participant,N,N,N,N,Y,N,N,N,N
Dylan,Bourne,dylan@example.com,uuid2,label_participant,N,N,N,N,Y,N,N,N,N
Dylan,Bourne,dylan@example.com,uuid3,label_participant,N,N,N,N,Y,N,N,N,N
Alice,Smith,alice@example.com,uuid1,label_participant,N,N,N,N,Y,N,N,N,N
Alice,Smith,alice@example.com,uuid2,label_participant,N,N,N,N,Y,N,N,N,N
Alice,Smith,alice@example.com,uuid3,label_participant,N,N,N,N,Y,N,N,N,N
```

**Send-Email CSV:**
Create ONE row per user (use the first LabelParticipant UUID for each):
```
<first_name>,<last_name>,<email>,<first_lp_uuid>,label_participant,N,N,N,N,Y,N,N,N,Y
```

Example with 2 users = 2 rows:
```
Dylan,Bourne,dylan@example.com,uuid1,label_participant,N,N,N,N,Y,N,N,N,Y
Alice,Smith,alice@example.com,uuid1,label_participant,N,N,N,N,Y,N,N,N,Y
```

Save both files to `~/Downloads/`:
- `~/Downloads/<output_name>.csv`
- `~/Downloads/<output_name>-Send-Email.csv`

### Step 5: Upload No-Email CSV to S3

**Important:** Use the `aws-with-awsume` wrapper script (located at `~/.local/bin/aws-with-awsume`) to run AWS CLI commands with awsume credentials. This wrapper handles credential sourcing automatically.

Test credentials:
```bash
aws-with-awsume prod sts get-caller-identity
```

If this fails, the user may need to run `awsume prod` first to authenticate with MFA.

Upload the no-email CSV:
```bash
aws-with-awsume prod s3 cp ~/Downloads/<output_name>.csv s3://prod-altafonte-user-ingestion/
```

### Step 6: Monitor Step Function (No-Email)

Wait 2-3 seconds for the S3 trigger to fire, then get the latest execution:

```bash
sleep 3
EXECUTION_ARN=$(aws-with-awsume prod stepfunctions list-executions \
  --state-machine-arn arn:aws:states:us-east-1:437795906767:stateMachine:prod-workflow-altafonte-user-creation \
  --max-results 1 \
  --query 'executions[0].executionArn' \
  --output text)
```

Poll the execution status every 5 seconds until completion:
```bash
while true; do
    # Get status and extract just the status word (filter out escape sequences)
    STATUS=$(aws-with-awsume prod stepfunctions describe-execution \
        --execution-arn "$EXECUTION_ARN" \
        --query 'status' \
        --output text | grep -o -E '(RUNNING|SUCCEEDED|FAILED|TIMED_OUT|ABORTED)' | tail -1)

    echo "Status: $STATUS"

    if [[ "$STATUS" == "SUCCEEDED" ]]; then
        echo "✓ Step function succeeded"
        break
    elif [[ "$STATUS" == "FAILED" || "$STATUS" == "TIMED_OUT" || "$STATUS" == "ABORTED" ]]; then
        echo "✗ Step function $STATUS"
        exit 1
    fi

    sleep 5
done
```

**Important:** The wrapper script may output terminal escape sequences. Use `grep -o -E` to extract only the status word for reliable comparison.

Possible statuses:
- `RUNNING`: Continue polling
- `SUCCEEDED`: Move to next step
- `FAILED`, `TIMED_OUT`, `ABORTED`: Report error and stop

Provide the user with the console URL:
```
https://us-east-1.console.aws.amazon.com/states/home?region=us-east-1#/v2/executions/details/$EXECUTION_ARN
```

### Step 7: Upload Send-Email CSV to S3

If `skip_email` is false, upload the send-email CSV:
```bash
aws-with-awsume prod s3 cp ~/Downloads/<output_name>-Send-Email.csv s3://prod-altafonte-user-ingestion/
```

### Step 8: Monitor Step Function (Send-Email)

Repeat the monitoring process from Step 6 using `aws-with-awsume prod` for all AWS CLI commands.

### Step 9: Query Neo4j for Identity IDs

Use the `mcp__neo4j-prod__prod-read_neo4j_cypher` tool to get Identity IDs for **all users**:

Build an array of all user emails and query:

```cypher
WITH ['email1@example.com', 'email2@example.com', 'email3@example.com'] AS emails
UNWIND emails AS email
MATCH (i:Identity)-[:HAS_PROFILE]->(p:Profile {profileType: "InsightsProfile"})
WHERE i.email = email
RETURN i.id
ORDER BY apoc.coll.indexOf(emails, i.email)
```

This will return Identity IDs in the same order as the input emails.

### Step 10: Generate Harness Segment CSV

Create a CSV file with **NO HEADER** containing just the Identity IDs (one per line) for **all users**:

```
<identity_id_1>
<identity_id_2>
<identity_id_3>
...
```

**Important:** The file must NOT have any header row - only Identity IDs, one per line. Adding a header will cause the upload to fail.

Save to: `~/Downloads/<output_name>-Harness.csv`

### Step 11: Manual Harness Segment Upload

**This step requires manual action in the Harness UI.**

1. Open the Sony_Users_Bulk_Import segment definition page:
   ```
   https://app.harness.io/ng/account/cej_iP27SSSgxFiM6TOjAw/all/fme/orgs/TheOrchardFME/projects/Default/org/5b4b5c30-21c9-11ea-a4e7-0a9b522eabbd/ws/5b5212f0-21c9-11ea-a4e7-0a9b522eabbd/segments/a4bc5637-86ee-11ef-bacb-627fbfefd41c/env/5b53e7b0-21c9-11ea-a4e7-0a9b522eabbd/definition
   ```

2. Upload the Harness CSV file:
   - Click the upload button on the segment definition page
   - Select `~/Downloads/<output_name>-Harness.csv`
   - Wait for upload confirmation
   - Verify the Identity ID count increased

3. The segment is used by the `show_sme_data` feature flag to control access to Insights SME features.

**Why Manual?**
The Harness MCP API does not currently support programmatic segment updates. While the API theoretically supports updating segment definitions, retrieving the current segment structure to safely merge new Identity IDs is not possible through available API endpoints. Attempting blind updates risks breaking the production segment.

### Step 12: Summary

Report to the user:
- All generated file paths
- Number of users processed
- Number of LabelParticipants found
- Number of Identity IDs retrieved
- Step function execution results
- Harness segment URL for manual upload
- Next steps

**Example output for bulk request:**
```
✓ Successfully created 6 users with access to 34 LabelParticipants

Generated files:
  • ~/Downloads/Insights-Insanity-Records-2026-04-16.csv (204 rows)
  • ~/Downloads/Insights-Insanity-Records-2026-04-16-Send-Email.csv (6 rows)
  • ~/Downloads/Insights-Insanity-Records-2026-04-16-Harness.csv (6 identity IDs)

Next steps:
  1. Upload Identity IDs to Harness segment:
     https://app.harness.io/ng/account/cej_iP27SSSgxFiM6TOjAw/all/fme/orgs/TheOrchardFME/projects/Default/org/5b4b5c30-21c9-11ea-a4e7-0a9b522eabbd/ws/5b5212f0-21c9-11ea-a4e7-0a9b522eabbd/segments/a4bc5637-86ee-11ef-bacb-627fbfefd41c/env/5b53e7b0-21c9-11ea-a4e7-0a9b522eabbd/definition

     Upload file: ~/Downloads/Insights-Insanity-Records-2026-04-16-Harness.csv

  2. Verify users can access Insights at https://insights.sonymusic.com
```

### Future Enhancement: Automated Harness Upload

**Status:** Investigated but not currently feasible.

**Investigation findings (2026-05-06):**
- The `fme_rule_based_segment_definition` resource supports UPDATE operations via Harness MCP
- However, retrieving the current segment structure is not supported through any available API endpoint
- Blind updates risk overwriting the existing segment configuration
- The API returns empty results for segment list operations
- Direct segment retrieval by ID or name fails consistently

**When API access improves:**
This step could be automated by:
1. Retrieving the current segment definition via API
2. Merging new Identity IDs with existing IDs
3. Updating the segment definition programmatically
4. Verifying the update succeeded

**Alternative approach if CSV upload API becomes available:**
The Harness/Split.io platform may provide a CSV upload API endpoint in the future, which would allow programmatic uploads without needing to construct the segment definition object.

## Error Handling

- **No LabelParticipants found**: Report warning and ask user to verify the GlobalParticipant ID
- **AWS credentials expired**: Instruct user to run `awsume prod`
- **Step function failed**: Provide console URL and suggest checking logs
- **Neo4j query error**: Report error and suggest checking Neo4j connectivity

## Important Notes

- This skill ONLY creates users in PROD (not QA)
- The CSV filename should be descriptive for auditing purposes
- Analytics permission is set to `Y`, all other permissions are set to `N`
- TenantType is always `label_participant`
- The user will still need to manually upload the Harness CSV to the segment
