---
name: demo-upload
description: Upload generated billing data to a CloudZero organization.
---

# /demo-upload

You are uploading generated billing data to CloudZero for an SE. You scan the
demo's config tree, discover datasets, write all upload configs, then let the
SE choose what to upload and where.

## Context

- **Workspace**: the current directory is a czbg-workspace created by `czbg init`
- **Configs**: organized as `configs/{demo-name}/{provider}/{category}/generate/` (created by `/demo-plan`)
- **Upload command**: `czbg upload generated --config <path>`
- **Stage discovery**: upload auto-selects the highest-priority stage with data
  (`variance` > `generate`). No manual stage selection needed for the common case.
- **Organizations**: API keys stored as env vars in `~/.czbg/.env`
  (format: `CZ_ORG_API_KEY_{ORG_NAME}`)
- **Schedules**: the upload command automatically splits months into past/present
  (uploaded immediately) and future (registered with the scheduler for automatic
  upload when each month becomes current). This prevents rate-limit contention
  at CloudZero ingest.

## Rules

1. **Never modify generate configs** — if generation needs re-running, tell the
   SE to use `/demo-generate`.
2. **Connection suffix is mandatory and auto-derived** — never ask the SE for it.
   Format: `{DemoName}` for base, `{DemoName}-{Category}` for non-base.
   Examples: `AcmeCorp`, `AcmeCorp-Anomalies`, `AcmeCorp-Optimize`.
3. **Always confirm before submitting** — present a summary of what will be
   uploaded and let the SE select datasets, months, and target org(s).
4. **Write upload configs next to generate configs** — place them at
   `configs/{demo-name}/{provider}/{category}/upload/{provider}-{demo-name}-{category}.upload.yaml`.
5. **Never wait for job completion** — uploads can take 10-30+ minutes. Report
   job IDs and monitoring commands, then stop.

## Arguments

`/demo-upload [demo-name]`

- Optional demo name. If omitted, list available demos.

The argument string is: $ARGUMENTS

## Workflow

### Step 1: Identify Demo

If the SE provided a demo name in `$ARGUMENTS`, resolve to `configs/{demo-name}/`.

Otherwise, list available demos:

```bash
ls -d configs/*/
```

### Step 2: Scan Config Tree

Find all provider/category combinations that have generate configs:

```bash
find configs/{demo-name} -path "*/generate/*.yaml" -type f
```

Build a list of `{provider}/{category}` pairs from the paths found.

### Step 3: Discover Datasets

List available datasets and match them to this demo by description:

```bash
czbg dataset list
```

For each matched dataset, get details:

```bash
czbg dataset describe <dataset_id>
```

This gives you: available months, providers, stages completed, and cost totals.

If no datasets match the demo name, ask the SE for the dataset ID(s) directly.

### Step 4: Write All Upload Configs

For each provider/category combo found in Step 2, write an upload config.

**File path:**
```
configs/{demo-name}/{provider}/{category}/upload/{provider}-{demo-name}-{category}.upload.yaml
```

For base configs, drop the category from the filename:
```
configs/{demo-name}/{provider}/base/upload/{provider}-{demo-name}.upload.yaml
```

**Connection suffix derivation** (mandatory, never ask the SE):
- Convert demo name to PascalCase: `acme-corp` → `AcmeCorp`
- Base category: just the demo name → `AcmeCorp`
- Non-base category: append category → `AcmeCorp-Anomalies`, `AcmeCorp-Optimize`

**How connections work** — each upload creates a new CloudZero connection with an
auto-generated name: `{provider}_{suffix}_bc_0001_{timestamp}` (e.g.,
`aws_AcmeCorp_bc_0001_20260421-1530`). Uploads never overwrite existing connections.
The suffix groups related data logically within an org, so different demos stay
isolated. To avoid connection sprawl when re-uploading, delete old connections
first: `czbg connection delete <org> <connection-id>`.

**Template:**

```yaml
upload_config:
  dataset_name: "<dataset_id>"
  connection_suffix: "<derived-suffix>"
  months:
    - "2026-01"
    - "2026-02"
  providers:
    - AWS
  organizations:
    <ORG_NAME>:
      api_token_env: "CZ_ORG_API_KEY_<ORG_NAME>"
```

**Stage overrides** — only add when bypassing auto-discovery:
- Anomaly and optimize configs should use `stage_overrides: { <PROVIDER>: "generate" }`
  since they skip the variance pass.
- Base configs need no override — auto-discovery selects `variance` over `generate`.

Leave the `organizations` block empty for now — the SE selects orgs in Step 5.

### Step 5: Present and Confirm

Show the SE a summary table of everything discovered:

```
Upload Plan for acme-corp:

  Dataset                     Provider  Category   Months       Suffix           
  2026-04-10_21-26-40-895     AWS       base       2026-01..12  AcmeCorp         
  2026-04-10_21-26-40-895     GCP       base       2026-01..12  AcmeCorp         
  2026-04-11_09-15-33-112     AWS       optimize   2026-01..12  AcmeCorp-Optimize
  2026-04-11_10-22-01-445     AWS       anomalies  2026-01..12  AcmeCorp-Anomalies
```

Then ask:

1. **Which of these do you want to upload?** (default: all)
2. **Which months?** (default: all available — note that future months will be
   scheduled automatically, not uploaded immediately)
3. **Which CloudZero org(s)?** List available orgs:

   ```bash
   grep "CZ_ORG_API_KEY_" ~/.czbg/.env | sed 's/CZ_ORG_API_KEY_\([^=]*\)=.*/\1/'
   ```

Update the upload configs with the SE's selections (orgs, month overrides).

### Step 6: Submit

Submit all selected upload jobs:

```bash
czbg upload generated --config <path1>
czbg upload generated --config <path2>
# ... one per upload config
```

If any command fails, show the error and continue with the remaining uploads.
Common fixes:
- Auth error → `aws sso login --profile cz-test-env-1.AdministratorAccess`
- Missing dataset → "Verify dataset_id with `czbg dataset list`"
- Missing API key → "Check `~/.czbg/.env` for `CZ_ORG_API_KEY_<ORG>`"

### Step 7: Report

After all submissions:

```
Upload jobs submitted:

  Job ID       Config                                    Status
  a3f7b2c4     aws-acme-corp.upload.yaml                 submitted
  b8e2d1f6     gcp-acme-corp.upload.yaml                 submitted
  c5a9e3b2     aws-acme-corp-optimize.upload.yaml        submitted

Monitor progress:
  czbg jobs list
  czbg jobs describe <job_id>

Note: Past/current months upload immediately. Future months are registered
with the scheduler and will upload automatically when each month becomes
current. This prevents rate-limit issues at CloudZero ingest.

After uploads complete, verify in CloudZero:
  1. Log in to CloudZero (<org_name>)
  2. Navigate to Cost Explorer
  3. Select the uploaded month range
  4. Verify data appears for the expected providers
```
