# Agnosticv:catalog Builder

> Create or update AgnosticV catalog files (common.yaml, dev.yaml, description.adoc, info-message-template.adoc)

- Skill: `majiayu000/agnosticv-catalog-builder` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/agnosticv-catalog-builder`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/agnosticv-catalog-builder/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/agnosticv-catalog-builder

---


---
context: main
model: sonnet
---

# Skill: agnosticv-catalog-builder

**Name:** AgnosticV Catalog Builder
**Description:** Create or update AgnosticV catalog files for RHDP deployments
**Version:** 2.1.0
**Last Updated:** 2026-02-03

---

## ⚠️ CRITICAL: Keep Questions Simple and Direct

**When asking for paths, URLs, or locations:**
- Just ask for the path or URL directly
- DO NOT ask about GitHub organizations, root folders, subdirectories, or try to find/detect them
- DO NOT offer multiple options to search or auto-detect
- Accept exactly what the user provides - don't try to be smart about it

**Example of what NOT to do:**
```
❌ Which GitHub organization? (rhpds / redhat-scholars / Other)
❌ Should I auto-detect your catalog directory? [Y/n]
❌ Which subdirectory should I use?
```

**Example of what TO do:**
```
✅ What is the URL or path to your Showroom repository?
✅ What is the path to the catalog/CI directory?
```

Just ask, use what they give you, move on. Users know their paths - trust them.

---

## Purpose

Unified skill for creating and updating AgnosticV catalog configurations. Handles everything from full catalog creation to updating individual files like description.adoc or info-message-template.adoc.

## Workflow Diagram

![Workflow](workflow.svg)

## What You'll Need Before Starting

Have these ready before running this skill:

**Choose your mode first:**
1. **Full Catalog** - Creating complete new catalog
2. **Description Only** - Just updating description.adoc
3. **Info Message Template** - Creating info-message-template.adoc
4. **Virtual CI** - Creating published/ Virtual CI

**For Full Catalog mode:**
- 📁 **AgnosticV repo path** - Where your local AgnosticV repository is (e.g., `~/work/code/agnosticv`)
- 🏢 **Catalog details**:
  - Display name (appears in RHDP UI)
  - Short name (directory name, lowercase with hyphens)
  - Brief description (1-2 sentences)
  - Category (workshop, demo, integration, etc.)
- 🔧 **Infrastructure choices**:
  - Cloud provider (AWS, Azure, OpenShift CNV, None)
  - Sandbox architecture (single-node, multi-node)
  - Workloads needed (which ocp4_workload_* roles)
- 🔗 **Showroom URL** (optional) - Link to your workshop/demo repository

**For Description Only mode:**
- 📁 **Showroom path or URL** - Where your workshop content is
- 📁 **Catalog directory path** - Where to save description.adoc
- 📋 **Module content** - Completed Showroom modules to extract from

**For Info Message Template:**
- 📁 **Catalog path** - Path to existing AgV catalog
- 📊 **User data keys** - List of data your workload shares via agnosticd_user_info

**For Virtual CI:**
- 📁 **Base component path** - Existing component to create Virtual CI from
- 🏷️ **Naming** - Virtual CI folder name (must be unique)

**Access needed:**
- ✅ Write permissions to AgnosticV repository
- ✅ Git configured with SSH access to GitHub
- ✅ RHDP account with AgnosticV repository access

---

## When to Use This Skill

Use `/agnosticv-catalog-builder` when you need to:

- Create a complete new RHDP catalog item
- Generate just description.adoc from Showroom content
- Create info-message-template.adoc for user data display
- Update catalog files after Showroom content changes
- Set up infrastructure provisioning for workshops/demos

**Prerequisites:**
- RHDP account with AgnosticV repository access
- AgnosticV repository cloned locally (e.g., `~/work/code/agnosticv` or `~/devel/git/agnosticv`)
- Git configured with SSH access to GitHub
- For description generation: Showroom content in `content/modules/ROOT/pages/`

---

## Skill Workflow Overview

```
Step 0: Prerequisites & Scope Selection
  ↓
  ├─ Full Catalog → Steps 1-10 (infrastructure + all files)
  ├─ Description Only → Steps 1-3 (extract from Showroom)
  └─ Info Message Template → Steps 1-2 (user data template)
```

---

## Step 0: Prerequisites & Scope Selection (FIRST)

**CRITICAL:** Start by asking what the user wants to generate.

### Ask for Scope

```
🏗️  AgnosticV Catalog Builder

What would you like to create or update?

1. Full Catalog (common.yaml, dev.yaml, description.adoc, info-message-template.adoc)
   └─ For: New catalog from scratch with infrastructure setup

2. Description Only (description.adoc)
   └─ For: Generate/update description from Showroom content

3. Info Message Template (info-message-template.adoc)
   └─ For: Display user data from your workload CI

4. Create Virtual CI (published/ folder)
   └─ For: Create Virtual CI from existing base component

Your choice [1-4]:
```

### Get AgnosticV Repository Path

**Option C: Read from configuration files if present, otherwise ask**

Checks these locations in order:
1. `~/CLAUDE.md`
2. `~/claude/*.md`
3. `~/.claude/*.md`

```bash
# Check configuration files for AgV path (multiple locations)
agv_path=""

# Check ~/CLAUDE.md first
if [[ -f ~/CLAUDE.md ]]; then
  agv_path=$(grep -E "agnosticv.*:" ~/CLAUDE.md | grep -oE '(~|/)[^ ]+' | head -1)
fi

# Check ~/claude/*.md if not found
if [[ -z "$agv_path" ]]; then
  for file in ~/claude/*.md; do
    [[ -f "$file" ]] && agv_path=$(grep -E "agnosticv.*:" "$file" | grep -oE '(~|/)[^ ]+' | head -1)
    [[ -n "$agv_path" ]] && break
  done
fi

# Check ~/.claude/*.md if still not found
if [[ -z "$agv_path" ]]; then
  for file in ~/.claude/*.md; do
    [[ -f "$file" ]] && agv_path=$(grep -E "agnosticv.*:" "$file" | grep -oE '(~|/)[^ ]+' | head -1)
    [[ -n "$agv_path" ]] && break
  done
fi

# Expand tilde if present
[[ "$agv_path" =~ ^~ ]] && agv_path="${agv_path/#\~/$HOME}"
```

**If found in configuration:**
```
✓ Found AgV path in configuration: [path from configuration]

Proceeding with this path.
```

**If NOT found, ask:**
```
📂 AgnosticV Repository

Q: What is your AgnosticV repository directory path?
   (e.g., ~/work/code/agnosticv or ~/devel/git/agnosticv)

Your path:
```

**Validate path exists:**
```bash
# Expand tilde if present
[[ "$agv_path" =~ ^~ ]] && agv_path="${agv_path/#\~/$HOME}"

# Check directory exists
if [[ ! -d "$agv_path" ]]; then
  echo "✗ Directory not found: $agv_path"
  exit 1
fi

# Check if it's a git repo (warning only)
if [[ ! -d "$agv_path/.git" ]]; then
  echo "⚠️  Warning: Not a git repository"
fi

echo "✓ Using: $agv_path"
```

### Git Branch Selection

```bash
cd "$agv_path"

# Show current branch
current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
```

**Ask about branch:**
```
📍 I see you are working on branch: $current_branch

Q: Do you want to use this branch or should I create a new one?

1. Use current branch: $current_branch
2. Create new branch

Choice [1/2]:
```

**If user chooses 1 (Use current branch):**
```
✓ Using your current branch: $current_branch
```

**If user chooses 2 (Create new branch):**
```
Q: New branch name (e.g., add-my-catalog or update-description):
   (no 'feature/' prefix needed)

Branch name:
```

```bash
# Strip feature/ prefix if user added it
branch_name="${branch_name#feature/}"

# Create and switch to new branch
git checkout -b "$branch_name"

echo "✓ Created and switched to branch: $branch_name"
```

---

## MODE 1: Full Catalog Creation

**When selected:** User chose option 1 (Full Catalog)

### Step 1: Catalog Discovery (Search Existing)

Search for similar catalogs to learn from or use as reference.

```
📖 Catalog Discovery

Let me search for existing catalogs to help you.

Q: What keywords describe your catalog?
   (Examples: ansible, openshift ai, pipelines, gitops)

Keywords:
```

**Search logic:**
```bash
cd $AGV_PATH

# Search by keywords in:
# - Directory names
# - common.yaml display names
# - description.adoc content

find . -type f -name "common.yaml" | while read file; do
  dir=$(dirname "$file")
  name=$(grep "^name:" "$file" | cut -d':' -f2-)
  echo "$dir: $name"
done | grep -i "$keywords"
```

**Present results and ask:**
```
Found similar catalogs:

1. agd_v2/ansible-aap-workshop/
   └─ Ansible Automation Platform Self-Service

2. agd_v2/openshift-gitops-intro/
   └─ OpenShift GitOps Introduction

Do you want to:
A. Create new catalog from scratch
B. Use one as reference (copy structure)
C. Just browse and continue

Choice [A/B/C]:
```

### Step 2: Category Selection (REQUIRED)

```
📂 Category Selection

RHDP catalogs MUST have exactly one category:

1. Workshops - Multi-user hands-on learning with exercises
2. Demos - Single-user presenter-led demonstrations
3. Labs - General learning environments
4. Sandboxes - Self-service playground environments
5. Brand_Events - Events like Red Hat Summit, Red Hat One

Q: Which category? [1-5]:
```

**Validate:** Must be one of: `Workshops`, `Demos`, `Labs`, `Sandboxes`, `Brand_Events` (exact match)

**Important validation rules:**
- Workshops/Brand_Events → Must set multiuser: true
- Demos → Must set multiuser: false (single-user only)

### Step 3: UUID Generation (REQUIRED)

```
🔑 UUID Generation

Every catalog needs a unique RFC 4122 compliant UUID.

Generating UUID...
```

**Generate and validate:**
```bash
# Generate lowercase UUID
new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]')

# Check for collisions
echo "Generated: $new_uuid"
echo "Checking for collisions..."

# Search all common.yaml files
grep -r "asset_uuid: $new_uuid" $AGV_PATH/ --exclude-dir=.git

if [[ $? -eq 0 ]]; then
  echo "⚠️  Collision detected! Regenerating..."
  # Regenerate until unique
fi
```

### Step 4: Infrastructure Selection

```
🏗️  Infrastructure Selection

Choose your OpenShift deployment type:

1. CNV Multi-Node (Standard - Recommended for most)
   └─ Full OpenShift cluster on CNV pools
   └─ Best for: Multi-user workshops, complex workloads
   └─ Component: agd-v2/ocp-cluster-cnv-pools/prod

2. SNO (Single Node OpenShift)
   └─ Single-node cluster for lightweight demos
   └─ Best for: Quick demos, single-user environments, edge demos
   └─ Component: agd-v2/ocp-cluster-cnv-pools/prod (cluster_size: sno)

3. AWS (Cloud-based VMs)
   └─ VM-based deployment on AWS
   └─ Best for: GPU workloads, AWS-specific features, bastion-only demos
   └─ Component: Custom (requires bastion + instances configuration)

4. CNV VMs (Cloud VMs on CNV)
   └─ Virtual machines on CNV infrastructure
   └─ Best for: RHEL demos, edge appliances, non-OpenShift workloads
   └─ Component: Custom (cloud_provider: openshift_cnv, config: cloud-vms-base)

Q: Infrastructure choice [1-4]:
```

**Set configuration based on choice:**

1. **CNV Multi-Node:**
```yaml
config: openshift-workloads
cloud_provider: none
clusters:
  - default:
      api_url: "{{ openshift_api_url }}"
      api_token: "{{ openshift_cluster_admin_token }}"

__meta__:
  components:
    - name: openshift
      display_name: OpenShift Cluster
      item: agd-v2/ocp-cluster-cnv-pools/prod
      parameter_values:
        cluster_size: multinode
        host_ocp4_installer_version: "4.20"
        ocp4_fips_enable: false
        num_users: "{{ num_users }}"
```

2. **SNO:**
```yaml
config: openshift-workloads
cloud_provider: none
clusters:
  - default:
      api_url: "{{ openshift_api_url }}"
      api_token: "{{ openshift_cluster_admin_token }}"

__meta__:
  components:
    - name: openshift
      display_name: OpenShift Cluster
      item: agd-v2/ocp-cluster-cnv-pools/prod
      parameter_values:
        cluster_size: sno
        host_ocp4_installer_version: "4.20"
        ocp4_fips_enable: false
```

3. **AWS:**
```yaml
cloud_provider: aws
config: cloud-vms-base

# Define security groups and instances
security_groups:
  - name: BastionSG
    rules:
      - name: SSH
        from_port: 22
        to_port: 22
        protocol: tcp
        cidr: "0.0.0.0/0"
        rule_type: Ingress

instances:
  - name: bastion
    count: 1
    image: RHEL-10.0-GOLD-latest
    flavor:
      ec2: t3a.large
    security_groups:
      - BastionSG
```

4. **CNV VMs:**
```yaml
cloud_provider: openshift_cnv
config: cloud-vms-base

instances:
  - name: bastion
    count: 1
    image: rhel-9.6
    cores: 8
    memory: 16G
    image_size: 200Gi
```

### Step 5: Authentication Setup

```
🔐 Authentication Method

How should users authenticate to OpenShift?

1. htpasswd (Simple username/password - Most common)
   └─ Workload: agnosticd.core_workloads.ocp4_workload_authentication_htpasswd

2. Keycloak SSO (Enterprise authentication - For AAP/complex setups)
   └─ Workload: agnosticd.core_workloads.ocp4_workload_authentication_keycloak

Q: Authentication method [1-2]:
```

**Set workload based on choice:**

1. **htpasswd:**
```yaml
workloads:
  - agnosticd.core_workloads.ocp4_workload_authentication_htpasswd
  # ... other workloads

# htpasswd configuration
common_password: "{{ (guid[:5] | hash('md5') | int(base=16) | b64encode)[:8] }}"
ocp4_workload_authentication_htpasswd_admin_user: admin
ocp4_workload_authentication_htpasswd_admin_password: "{{ common_password }}"
ocp4_workload_authentication_htpasswd_user_base: user
ocp4_workload_authentication_htpasswd_user_password: "{{ common_password }}"
ocp4_workload_authentication_htpasswd_user_count: "{{ num_users | default('1') }}"
ocp4_workload_authentication_htpasswd_remove_kubeadmin: true
```

2. **Keycloak:**
```yaml
workloads:
  - agnosticd.core_workloads.ocp4_workload_authentication_keycloak
  # ... other workloads

# Keycloak configuration
common_password: "{{ (guid[:5] | hash('md5') | int(base=16) | b64encode)[:8] }}"
ocp4_workload_authentication_keycloak_namespace: keycloak
ocp4_workload_authentication_keycloak_channel: stable-v26.2
ocp4_workload_authentication_keycloak_admin_username: admin
ocp4_workload_authentication_keycloak_admin_password: "{{ common_password }}"
ocp4_workload_authentication_keycloak_num_users: "{{ num_users }}"
ocp4_workload_authentication_keycloak_user_username_base: user
ocp4_workload_authentication_keycloak_user_password: "{{ common_password }}"
ocp4_workload_authentication_keycloak_remove_kubeadmin: true
```

### Step 6: Workload Selection

```
🧩 Workload Selection

I'll recommend workloads based on your catalog.

Q: What technologies will users learn? (comma-separated)
   Examples: ansible aap, openshift ai, gitops, pipelines

Technologies:
```

**Workload recommendation engine:**

Based on keywords, suggest from workload-mappings.md:
- `ansible` or `aap` → `rhpds.aap25.ocp4_workload_aap25`
- `ai` or `gpu` → `rhpds.nvidia_gpu.ocp4_workload_nvidia_gpu`
- `gitops` or `argocd` → `rhpds.openshift_gitops.ocp4_workload_openshift_gitops`
- `pipelines` → `rhpds.openshift_pipelines.ocp4_workload_openshift_pipelines`
- `showroom` → `rhpds.showroom.ocp4_workload_showroom` (always recommended)

**Present recommendations:**
```
Recommended workloads:

✓ rhpds.ocp4_workload_authentication.ocp4_workload_authentication (selected - auth)
✓ rhpds.showroom.ocp4_workload_showroom (recommended - guide)
  rhpds.aap25.ocp4_workload_aap25 (suggested - ansible)
  rhpds.openshift_gitops.ocp4_workload_openshift_gitops (suggested - gitops)

Select workloads (comma-separated numbers, or 'all'):
```

### Step 7: Showroom Repository Detection

**Ask directly for the Showroom URL or path. DO NOT ask about GitHub org, root folders, or try to find it yourself:**

```
📚 Showroom Content

Q: Do you have a Showroom repository for this catalog? [Y/n]
```

**If YES:**
```
Q: What is the URL or path to your Showroom repository?

   Just provide the URL or path - I'll use it as-is.

   Examples:
   - https://github.com/rhpds/showroom-ansible-ai
   - /path/to/local/showroom

URL or path:
```

**If NO:**
```
ℹ️  You can add the Showroom URL later in common.yaml
```

### Step 8: Catalog Details

```
📝 Catalog Details

Q: Display name (appears in RHDP UI):
   Example: Ansible Automation Platform with OpenShift AI

Name:

Q: Short name (lowercase, hyphens, descriptive):
   Example: ansible-aap-ai-workshop

Short name:

Q: Brief description (1-2 sentences):
   This appears in the catalog listing.

Description:
```

**Validate directory doesn't exist across entire repo:**
```bash
# Check if directory name exists anywhere in AgV repo
if find "$AGV_PATH" -maxdepth 2 -type d -name "$short_name" 2>/dev/null | grep -q .; then
  echo "⚠️  Directory '$short_name' already exists in AgnosticV repo"
  echo "Choose a different name."
  exit 1
fi
```

### Step 8a: Repository Setup

```
📦 Repository Configuration
```

**Ask about Showroom repository:**
```
Q: Do you have a Showroom repository created for this catalog? [Y/n]
```

**If NO:**
```
📚 Create Showroom Repository

Use the Showroom Nookbag template to create your repository:

Repository: https://github.com/rhpds/showroom_template_nookbag

Instructions:
1. Visit: https://github.com/rhpds/showroom_template_nookbag
2. Follow the README to generate your showroom repository
3. Recommended naming: {short-name}-showroom
4. Create in: github.com/rhpds organization

Example:
  Use the nookbag template to create your repository:
  https://github.com/rhpds/showroom_template_nookbag

Once created, come back and re-run this skill with the repository URL.

⏸️  Pausing - Create your Showroom repository first.
```

**If YES:**
```
Q: What is the URL or path to your Showroom repository?

   Examples:
   - https://github.com/rhpds/ansible-aap-ai-showroom
   - /path/to/local/showroom

Showroom URL or path:
```

**Ask about custom Ansible collection:**
```
Q: Will this catalog use a custom Ansible collection? [Y/n]

ℹ️  Custom collections are needed when:
   - Creating new workloads specific to this catalog
   - Sharing workload logic across multiple catalogs
   - Building reusable automation components
```

**If YES:**
```
Collection naming: rhpds.{short-name}
Repository: https://github.com/rhpds/rhpds.{short-name}

Note:
- Collection must be created in github.com/rhpds organization
- Will be added to requirements_content in common.yaml
- Use this for catalog-specific workloads

Example structure:
  rhpds.{short-name}/
  ├── galaxy.yml
  ├── roles/
  │   └── ocp4_workload_{catalog_feature}/
  └── README.md
```

**If NO:**
```
✓ Using standard collections only (agnosticd.core_workloads, agnosticd.showroom, etc.)
```

### Step 9: Multi-User Configuration

```
👥 Multi-User Setup
```

**Auto-set based on category:**
- **Workshops / Brand_Events:** Multiuser REQUIRED
- **Demos:** Single-user only (multiuser: false)
- **Labs / Sandboxes:** Ask user

**If multi-user (Workshops/Brand_Events):**
```
Q: How many concurrent users (maximum)?
   Typical range: 10-60
   Default: 30

Max users [default: 30]:
```

**Set in common.yaml:**
```yaml
__meta__:
  catalog:
    multiuser: true
    workshopLabUiRedirect: true  # Auto-enable for workshops
    parameters:
      - name: num_users
        description: Number of users to provision within the environment
        formLabel: User Count
        openAPIV3Schema:
          type: integer
          default: 3
          minimum: 3
          maximum: 60
```

**Worker scaling formula (if CNV/SNO):**
```yaml
# Auto-scale workers based on num_users
openshift_cnv_scale_cluster: "{{ (num_users | int) > 3 }}"

# Per-user resource calculation
# Example: 1 worker per 5 users (adjust based on workload)
worker_instance_count: "{{ [(num_users | int / 5) | round(0, 'ceil') | int, 1] | max if (num_users | int) > 3 else 0 }}"

ai_workers_cores: 32
ai_workers_memory: 128Gi
```

**If single-user (Demos):**
```yaml
__meta__:
  catalog:
    multiuser: false  # Demos are always single-user
    # NO workshopLabUiRedirect for demos
```

### Step 10: Generate Files

Now generate all four files:

#### 10.1: Generate common.yaml

**Modern catalog structure (2026+):**

```yaml
---
#include /includes/agd-v2-mapping.yaml
#include /includes/sandbox-api.yaml
#include /includes/catalog-icon-openshift.yaml  # or catalog-icon-project-dance, catalog-icon-rh-ai-2025
#include /includes/terms-of-service.yaml
#include /includes/access-restriction-rh1-2026-devs.yaml  # Optional - for restricted catalogs

#include /includes/parameters/purpose.yaml
#include /includes/parameters/salesforce-id.yaml
#include /includes/parameters/num-users-salesforce.yaml  # If multi-user

#include /includes/secrets/ocp4_ai_offline_token.yaml
#include /includes/secrets/s3-rhpds-private-bucket.yaml

# -------------------------------------------------------------------
# --- Catalog Item: <Display Name>
# -------------------------------------------------------------------

# ===================================================================
# Repository Tag
# Tag for all repositories that are used in this config.
# ===================================================================
tag: main  # Override in prod.yaml, event.yaml with specific tag (e.g., <short-name>-1.0.0)

# -------------------------------------------------------------------
# Mandatory Variables
# -------------------------------------------------------------------
config: openshift-workloads
cloud_provider: none
software_to_deploy: none
openshift_cnv_scale_cluster: true
clusters:
  - default:
      api_url: "{{ openshift_api_url }}"
      api_token: "{{ openshift_cluster_admin_token }}"

# -------------------------------------------------------------------
# Platform
# -------------------------------------------------------------------
platform: rhpds

# -------------------------------------------------------------------
# CNV Specific (if using CNV pools)
# -------------------------------------------------------------------
bastion_instance_image: rhel-9.6
bastion_cores: 2
bastion_memory: 4Gi

# Worker scaling formula
worker_instance_count: "{{ [2, ((num_users | int / 5.0) | round(0, 'ceil') | int) + 1] | max }}"
ai_workers_cores: 32
ai_workers_memory: 128Gi

# -------------------------------------------------------------------
# Common Password
# -------------------------------------------------------------------
common_password: "{{ (guid[:5] | hash('md5') | int(base=16) | b64encode)[:8] }}"

# -------------------------------------------------------------------
# Student User on Bastion (if needed)
# -------------------------------------------------------------------
install_student_user: true
student_name: lab-user
student_sudo: true

# -------------------------------------------------------------------
# Custom collections for this environment
# -------------------------------------------------------------------
requirements_content:
  collections:
    - name: https://github.com/agnosticd/core_workloads.git
      type: git
      version: main
    - name: https://github.com/agnosticd/showroom.git
      type: git
      version: v1.3.9
    # Add other collections as needed

# -------------------------------------------------------------------
# Workloads
# -------------------------------------------------------------------
workloads:
  - agnosticd.core_workloads.ocp4_workload_authentication_htpasswd
  - agnosticd.showroom.ocp4_workload_showroom_ocp_integration
  - agnosticd.showroom.ocp4_workload_showroom
  # Add technology-specific workloads

# ===================================================================
# Workload: ocp4_workload_authentication_htpasswd
# ===================================================================
ocp4_workload_authentication_htpasswd_admin_user: admin
ocp4_workload_authentication_htpasswd_admin_password: "{{ common_password }}"
ocp4_workload_authentication_htpasswd_user_base: user
ocp4_workload_authentication_htpasswd_user_password: "{{ common_password }}"
ocp4_workload_authentication_htpasswd_user_count: "{{ num_users | default('1') }}"
ocp4_workload_authentication_htpasswd_remove_kubeadmin: true

# ===================================================================
# Workload: ocp4_workload_showroom
# ===================================================================
ocp4_workload_showroom_content_git_repo: https://github.com/rhpds/showroom-repo.git
ocp4_workload_showroom_content_git_repo_ref: main

# -------------------------------------------------------------------
# Metadata
# -------------------------------------------------------------------
__meta__:
  asset_uuid: <generated-uuid>
  anarchy:
    namespace: babylon-anarchy-7
  components:
    - name: openshift
      display_name: OpenShift Cluster
      item: agd-v2/ocp-cluster-cnv-pools/prod
      parameter_values:
        cluster_size: multinode  # or sno
        host_ocp4_installer_version: "4.20"
        ocp4_fips_enable: false
        num_users: "{{ num_users }}"
      propagate_provision_data:
        - name: sandbox_openshift_api_key
          var: sandbox_openshift_api_key
        - name: sandbox_openshift_api_url
          var: sandbox_openshift_api_url
        - name: sandbox_openshift_namespace
          var: sandbox_openshift_namespace
        - name: openshift_cluster_admin_token
          var: openshift_cluster_admin_token
        - name: openshift_api_url
          var: openshift_api_url
        - name: bastion_public_hostname
          var: bastion_ansible_host
        - name: bastion_ssh_user_name
          var: bastion_ansible_user
        - name: bastion_ssh_password
          var: bastion_ansible_ssh_pass
        - name: bastion_ssh_port
          var: bastion_ansible_port
  catalog:
    namespace: babylon-catalog-{{ stage | default('?') }}
    display_name: "<Display Name>"
    category: <Workshops|Demos|Labs|Sandboxes|Brand_Events>
    multiuser: true  # or false for demos
    workshopLabUiRedirect: true  # Only for workshops
    parameters:
      - name: num_users
        description: Number of users to provision within the environment
        formLabel: User Count
        openAPIV3Schema:
          type: integer
          default: 3
          minimum: 3
          maximum: 60
    keywords:
      - <keyword1>
      - <keyword2>
    labels:
      Provider: RHDP
      Product: <Product_Name>
      Product_Family: <Product_Family>
      # Brand_Event: Red_Hat_One_2026  # If Brand_Events
    reportingLabels:
      primaryBU: Hybrid_Platforms  # CRITICAL: For business unit tracking/reporting (Hybrid_Platforms, Application_Services, Ansible, RHEL, etc.)
  owners:
    maintainer:
      - email: <email>
        name: <Name>
    sme:
      - email: <sme-email>
        name: <SME Name>
  tower:
    timeout: 14400  # 4 hours - adjust based on complexity
  deployer:
    scm_url: https://github.com/agnosticd/agnosticd-v2
    scm_ref: main
    execution_environment:
      image: quay.io/agnosticd/ee-multicloud:chained-2025-12-17
      pull: missing
```

#### 10.2: Generate dev.yaml

```yaml
---
# -------------------------------------------------------------------
# Purpose - Cost tag. One of development, ilt, production, event
# -------------------------------------------------------------------
purpose: development
__meta__:
  deployer:
    scm_ref: main
    scm_type: git
```

**Note:** dev.yaml is minimal - only overrides scm_ref and sets purpose tag for cost tracking.

#### 10.3: Generate description.adoc

**Ask for description content:**
```
📄 Description Generation

I can extract description content from your Showroom, or you can provide it manually.

Q: Do you want me to extract from Showroom content? [Y/n]
```

**If YES and Showroom URL provided:**
```bash
# Clone showroom temporarily
temp_dir=$(mktemp -d)
git clone <showroom-url> "$temp_dir"

# Extract modules
find "$temp_dir/content/modules/ROOT/pages" -name "*.adoc" | sort

# Read module titles
grep "^= " "$temp_dir/content/modules/ROOT/pages"/*.adoc
```

**Generate description.adoc (following RHDP structure - Nate's format):**
```asciidoc
<Brief overview: 3-4 sentences max>
<Sentence 1: Start with product/technology name - what is this thing showing or doing?>
<Sentence 2-3: What is the intended use? Real-world scenario or business context>
<Do NOT mention the catalog name or generic info - get straight to the point>

[IMPORTANT]
====
<Add warnings AFTER overview if needed: GPU availability, beta/alpha release, limited support, high memory, etc.>
<For RH1 labs: "This Red Hat One lab is being released as-is with little change from the version delivered in person at the event. There is limited support for these labs and only those with regular usage will be retained in the RHDP catalog. A 30 day advance notice will be posted for any labs that get scheduled for removal.">
====

== Lab Guide

You can find a preview version of the link:<github-pages-url>[lab guide^] here.

== Featured Technology and Products

<List ONLY the products that matter or are the focus - max 6-7 for complex labs>
<Include major versions extracted from AgnosticV>
<List all significant products - e.g., for RHADS include RHTAS, RHTPA, RHACS, etc.>

* <Red Hat Product Name> <Major Version>
* <Red Hat Product Name> <Major Version>
* <Related Technology> <Major Version>

== Detailed Overview

* *<Module 1 Title>*
** <Key learning point 1>
** <Key learning point 2>
** <Key learning point 3>

* *<Module 2 Title>*
** <Key learning point 1>
** <Key learning point 2>
** <Key learning point 3>

* *<Module 3 Title>*
** <Key learning point 1>
** <Key learning point 2>

== Authors

<Retrieve all names from __meta__.owners in common.yaml - names only, NO emails>

* <Author Name>
* <Author Name>

== Support

For help with instructions or functionality, contact lab authors.

For problems with provisioning or environment stability:

* Open an RHDP link:https://red.ht/rhdp-ticket[Support Ticket^]
* Post a message in Slack channel: link:https://redhat.enterprise.slack.com/archives/C06QWD4A5TE[#forum-demo-redhat-com^]
```

**Example (Demo):**
```asciidoc
vLLM Playground demonstrates deploying and managing vLLM inference servers using containers with features like structured outputs, tool calling, and MCP integration. This demo uses the ACME Corporation customer support scenario to show how Red Hat AI Inference Server modernizes AI-powered infrastructure. Learners deploy vLLM servers, configure structured outputs for system integration, and implement agentic workflows with performance benchmarking.

[IMPORTANT]
====
GPU-enabled nodes recommended for optimal performance. CPU-only mode available but slower.
====

== Lab Guide

You can find a preview version of the link:https://rhpds.github.io/showroom-vllm-playground[lab guide^] here.

== Featured Technology and Products

* Red Hat Enterprise Linux 10
* vLLM Playground 0.1.1
* Red Hat AI

== Detailed Overview

* *Introduction to vLLM Playground*
** Overview of vLLM architecture and container-based deployment
** ACME Corp use case: modernizing customer support with AI
** Deploy first vLLM server instance

* *Structured Outputs Configuration*
** Configure JSON schema validation for reliable outputs
** Integrate with downstream systems using structured data
** Test output consistency across multiple requests

* *Tool Calling and MCP Integration*
** Implement function calling to extend AI capabilities
** Enable Model Context Protocol for agentic workflows
** Build human-in-the-loop approval system

* *Performance Benchmarking*
** Run load tests against vLLM deployments
** Compare CPU vs GPU performance metrics
** Validate production readiness criteria

== Authors

* Michael Tao
* Jane Developer

== Support

For help with instructions or functionality, contact lab authors.

For problems with provisioning or environment stability:

* Open an RHDP link:https://red.ht/rhdp-ticket[Support Ticket^]
* Post a message in Slack channel: link:https://redhat.enterprise.slack.com/archives/C06QWD4A5TE[#forum-demo-redhat-com^]
```

**Example (Workshop - Real from Nate's PR):**
```asciidoc
Red Hat Advanced Developer Suite (RHADS) scales platform engineering teams to increase developer productivity and reduce software supply chain risk in hybrid and multicloud environments. This hands-on workshop goes deep into the architecture and implementation of RHADS, built for solutions architects, consultants, and technical sellers. Learners deploy and configure core RHADS components, explore real-world integration patterns, and see how RHADS improves developer experience while strengthening software supply chain security.

[IMPORTANT]
====
This Red Hat One lab is being released as-is with little change from the version delivered in person at the event.
There is limited support for these labs and only those with regular usage will be retained in the RHDP catalog.
A 30 day advance notice will be posted for any labs that get scheduled for removal.
====

== Lab Guide

You can find a preview version of the link:https://rhpds.github.io/build-secured-dev-workflows-showroom[lab guide^] here.

== Featured Technology and Products

* Red Hat Advanced Developer Suite (RHADS)
* Red Hat Trusted Artifact Signer (RHTAS)
* Red Hat Trusted Profile Analyzer (RHTPA)
* Red Hat Developer Hub (RHDH)
* Red Hat Advanced Cluster Security (RHACS)
* Red Hat OpenShift Pipelines
* Jenkins

== Detailed Overview

* *Module 1: Establish Software Composition Trust with SBOMs*
** Configure identity and access management using Red Hat Build of Keycloak (RHBK)
** Deploy Red Hat Trusted Profile Analyzer (RHTPA) using an operator
** Ingest SBOMs and analyze software composition to gain visibility into vulnerabilities

* *Sign and Verify All Artifacts With RHTAS*
** Deploy Red Hat Trusted Artifact Signer (RHTAS) for cryptographic signing and verification
** Integrate RHTAS with Tekton Chains to automate keyless artifact signing
** Establish immutable provenance tracking with Fulcio, Rekor, and Trillian

* *Developer Workflow Without Developer Friction*
** Configure Red Hat Developer Hub (RHDH) with SSO authentication and dynamic plugins
** Set up catalog auto-discovery using GitOps and Configuration as Code
** Enable OIDC-based commit signing with GitSign and Red Hat DevSpaces

* *Enforce Policy and Promote Safely*
** Configure Red Hat Advanced Cluster Security (RHACS) integration with Quay for image scanning
** Execute end-to-end trusted software supply chain pipelines
** Enforce Enterprise Contract (EC) policies and promote only verified, compliant images

== Authors

* Tyrell Reddy
* Prakhar Srivastava
* Ramy El Essawy

== Support

For help with instructions or functionality, contact lab authors.

For problems with provisioning or environment stability:

* Open an RHDP link:https://red.ht/rhdp-ticket[Support Ticket^]
* Post a message in Slack channel: link:https://redhat.enterprise.slack.com/archives/C06QWD4A5TE[#forum-demo-redhat-com^]
```

**Key Guidelines (Nate's Format):**
- Brief overview: 3-4 sentences max - start with product name, what it shows, intended use, NO catalog name
- Warnings AFTER overview using `[IMPORTANT]` blocks (GPU, beta/alpha, limited support, etc.)
- Lab Guide section: "You can find a preview version of the link:url[lab guide^] here."
- Featured Products: 6-7 max for complex labs, include all significant products with major versions
- Detailed Overview: Use `**` (double asterisks) for sub-bullets under module titles, NOT numbered lists
- Do not create more than 3 sub-bullets for each module
- Module titles can use `*` for top-level bullets (like `* *Module 1: Title*`)
- Authors: names only from __meta__.owners in common.yaml - NO emails
- Support: Simple format - "For help with instructions or functionality, contact lab authors." then "For problems with provisioning or environment stability:" with RHDP ticket + Slack links

#### 10.4: Generate info-message-template.adoc

```
📧 Info Message Template

This template displays user data after deployment.

Q: Does your catalog use agnosticd_user_info to share data? [Y/n]
```

**If YES:**
```
Q: What data keys does your workload share via agnosticd_user_info.data?

Examples:
  - litellm_api_base_url
  - litellm_virtual_key
  - grafana_admin_password

Data keys (comma-separated):
```

**Generate info-message-template.adoc:**
```asciidoc
= Environment Provisioned

Your <catalog-name> environment has been provisioned successfully!

== Access Information

* *Console URL*: {openshift_console_url}
* *Username*: {openshift_cluster_admin_username}
* *Password*: {openshift_cluster_admin_password}

== Guide

Access your workshop guide:

* link:{openshift_cluster_console_url}/showroom[Workshop Guide^]

== Additional Information

=== <Service Name>

* *API URL*: {<data_key_1>}
* *API Key*: {<data_key_2>}

NOTE: The data above comes from `agnosticd_user_info.data` in your workload.

== Support

Questions? Reach out in Slack: [#forum-demo-developers](https://redhat.enterprise.slack.com/archives/C04MLMA15MX)
```

**Explain agnosticd_user_info:**
```
ℹ️  How to share data from your workload:

In your workload tasks/workload.yml:

- name: Save user data for info message
  agnosticd.core.agnosticd_user_info:
    data:
      my_api_url: "{{ api_base_url }}"
      my_api_key: "{{ virtual_key }}"

Then use in info-message-template.adoc:

* API URL: {my_api_url}
* API Key: {my_api_key}

The template renders when users receive their environment.
```

**If NO user data:**
```asciidoc
= Environment Provisioned

Your <catalog-name> environment is ready!

== Access Information

* *Console URL*: {openshift_console_url}
* *Username*: {openshift_cluster_admin_username}
* *Password*: {openshift_cluster_admin_password}

== Workshop Guide

link:{openshift_cluster_console_url}/showroom[Open Guide^]

== Support

Questions? [#forum-demo-developers](https://redhat.enterprise.slack.com/archives/C04MLMA15MX)
```

### Step 11: Determine Catalog Directory Path

```
📂 Catalog Directory Path

Q: Which subdirectory should I create the catalog in?

Common options:
- agd_v2 (standard catalogs)
- openshift_cnv (CNV-based catalogs)
- sandboxes-gpte (sandbox catalogs)
- published (Virtual CIs)
- custom path

Enter subdirectory (e.g., agd_v2):
```

**Build the path:**
```bash
# User enters subdirectory (e.g., "agd_v2")
catalog_path="$AGV_PATH/$subdirectory/$directory_name"
```

**Validate doesn't exist:**
```bash
if [[ -d "$catalog_path" ]]; then
  echo "⚠️  Directory already exists: $catalog_path"
  echo "Choose a different name or location."
  exit 1
fi
```

### Step 12: Write Files

```
💾 Writing Files

Creating catalog directory: $catalog_path

Writing:
  ✓ common.yaml
  ✓ dev.yaml
  ✓ description.adoc
  ✓ info-message-template.adoc
```

**Execute:**
```bash
mkdir -p "$catalog_path"

# Write all four files
cat > "$catalog_path/common.yaml" <<'EOF'
<generated-content>
EOF

cat > "$catalog_path/dev.yaml" <<'EOF'
<generated-content>
EOF

cat > "$catalog_path/description.adoc" <<'EOF'
<generated-content>
EOF

cat > "$catalog_path/info-message-template.adoc" <<'EOF'
<generated-content>
EOF
```

### Step 13: Git Commit (Optional)

```
🚀 Ready to Commit

Files created in: $catalog_path

Q: Commit these changes? [Y/n]
```

**If YES:**
```bash
# Get relative path from AgV root for commit message
rel_path="${catalog_path#$AGV_PATH/}"

cd "$AGV_PATH"

git add "$rel_path/"

git commit -m "Add $directory_name catalog

- Category: $category
- Infrastructure: $cloud_provider ($sandbox_architecture)
- Workloads: $num_workloads selected
- UUID: $asset_uuid
- Path: $rel_path"

current_branch=$(git rev-parse --abbrev-ref HEAD)
echo "✓ Committed to branch: $current_branch"
echo ""
echo "Next steps:"
echo "  1. Test locally: cd $rel_path && agnosticv_cli dev.yaml"
echo "  2. Run validator: /agnosticv-validator"
echo "  3. Create PR: git push origin $current_branch && gh pr create --fill"
```

---

## MODE 2: Description Only

**When selected:** User chose option 2 (Description Only)

**Simplified workflow - just extract from Showroom and generate description.adoc**

### Step 1: Locate Showroom

**Ask directly for the path. DO NOT ask about GitHub org or try to find it yourself:**

```
📚 Showroom Content

Q: What is the path to your Showroom repository?

   Just provide the path or URL - I'll use it as-is.
   Press Enter to use current directory.

   Examples:
   - /Users/you/work/my-workshop-showroom
   - https://github.com/rhpds/my-workshop-showroom
   - . (current directory)

Path:
```

**Validate:**
```bash
if [[ -d "$path/content/modules/ROOT/pages" ]]; then
  echo "✓ Found Showroom content"
  HAS

…(truncated)
