Back to skills
SkillHub ClubAnalyze Data & AIFull StackData / AI

swamp-model

Work with swamp models for AI-native automation. Use when searching for model types, describing model schemas, creating model inputs, running model methods, or viewing outputs. Triggers on "swamp model", "model type", "create input", "type search", "type describe", "run method", "execute method", "model validate", "model delete", "model edit", "model output", "output logs", "CEL expression", or running automation workflows.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
214
Hot score
97
Updated
March 20, 2026
Overall rating
C3.1
Composite score
3.1
Best-practice grade
B73.6

Install command

npx @skill-hub/cli install systeminit-swamp-swamp-model

Repository

systeminit/swamp

Skill path: .claude/skills/swamp-model

Work with swamp models for AI-native automation. Use when searching for model types, describing model schemas, creating model inputs, running model methods, or viewing outputs. Triggers on "swamp model", "model type", "create input", "type search", "type describe", "run method", "execute method", "model validate", "model delete", "model edit", "model output", "output logs", "CEL expression", or running automation workflows.

Open repository

Best for

Primary workflow: Analyze Data & AI.

Technical facets: Full Stack, Data / AI.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: systeminit.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install swamp-model into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/systeminit/swamp before adding swamp-model to shared team environments
  • Use swamp-model for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: swamp-model
description: Work with swamp models for AI-native automation. Use when searching for model types, describing model schemas, creating model inputs, running model methods, or viewing outputs. Triggers on "swamp model", "model type", "create input", "type search", "type describe", "run method", "execute method", "model validate", "model delete", "model edit", "model output", "output logs", "CEL expression", or running automation workflows.
---

# Swamp Model Skill

Work with swamp models through the CLI. All commands support `--json` for
machine-readable output.

## CRITICAL: Model Creation Rules

- **Never generate model IDs** — no `uuidgen`, `crypto.randomUUID()`, or manual
  UUIDs. Swamp assigns IDs automatically via `swamp model create`.
- **Never write a model YAML file from scratch** — always use
  `swamp model create <type> <name> --json` first, then edit the scaffold at the
  returned `path`, preserving the assigned `id`.
- **Never modify the `id` field** in an existing model file.
- **Verify CLI syntax**: If unsure about exact flags or subcommands, run
  `swamp help model` for the complete, up-to-date CLI schema.

Correct flow: `swamp model create <type> <name> --json` → set global args with
`--global-arg` or edit the YAML → validate → run.

## Quick Reference

| Task                | Command                                                            |
| ------------------- | ------------------------------------------------------------------ |
| Search model types  | `swamp model type search [query] --json`                           |
| Describe a type     | `swamp model type describe <type> --json`                          |
| Create model input  | `swamp model create <type> <name> --json`                          |
| Create with args    | `swamp model create <type> <name> --global-arg key=value --json`   |
| Search models       | `swamp model search [query] --json`                                |
| Get model details   | `swamp model get <id_or_name> --json`                              |
| Edit model input    | `swamp model edit [id_or_name]`                                    |
| Delete a model      | `swamp model delete <id_or_name> --json`                           |
| Validate model      | `swamp model validate [id_or_name] --json`                         |
| Validate by label   | `swamp model validate [id_or_name] --label policy --json`          |
| Validate by method  | `swamp model validate [id_or_name] --method create --json`         |
| Evaluate input(s)   | `swamp model evaluate [id_or_name] --json`                         |
| Run a method        | `swamp model method run <id_or_name> <method> --json`              |
| Run with inputs     | `swamp model method run <name> <method> --input '{}' -j`           |
| Skip all checks     | `swamp model method run <name> <method> --skip-checks -j`          |
| Skip check by name  | `swamp model method run <name> <method> --skip-check <n> -j`       |
| Skip check by label | `swamp model method run <name> <method> --skip-check-label <l> -j` |
| Search outputs      | `swamp model output search [query] --json`                         |
| Get output details  | `swamp model output get <output_or_model> --json`                  |
| View output logs    | `swamp model output logs <output_id> --json`                       |
| View output data    | `swamp model output data <output_id> --json`                       |

## Repository Structure

Swamp uses a dual-layer architecture:

- **Data directory (`/.swamp/`)** - Internal storage organized by entity type
- **Logical views (`/models/`)** - Human-friendly symlinked directories

```
/models/{model-name}/
  input.yaml → ../.swamp/definitions/{type}/{id}.yaml
  resource.yaml → ../.swamp/data/{type}/{id}/{specName}/latest/raw  (type=resource)
  outputs/ → ../.swamp/outputs/{type}/{id}/
  files/ → ../.swamp/data/{type}/{id}/ (filtered by type=file)
```

Use `swamp repo index` to rebuild if symlinks become out of sync.

## Search for Model Types

Find available model types in the system.

```bash
swamp model type search --json
swamp model type search "echo" --json
```

**Output shape:**

```json
{
  "query": "",
  "results": [
    { "raw": "command/shell", "normalized": "command/shell" }
  ]
}
```

## Describe Model Types

Get the full schema and available methods for a type.

```bash
swamp model type describe command/shell --json
```

**Output shape:**

```json
{
  "type": { "raw": "command/shell", "normalized": "command/shell" },
  "version": "2026.02.09.1",
  "globalArguments": {/* JSON Schema */},
  "resourceAttributesSchema": {/* JSON Schema */},
  "methods": [
    {
      "name": "execute",
      "description": "Execute a shell command and capture output",
      "arguments": {/* JSON Schema */}
    }
  ]
}
```

**Key fields:**

- `globalArguments` - JSON Schema for input YAML `globalArguments` section
- `methods` - Available operations with their per-method `arguments` schemas

## Create Model Inputs

```bash
swamp model create command/shell my-shell --json
```

Set globalArguments at creation time with `--global-arg` (repeatable):

```bash
swamp model create aws/ec2/vpc my-vpc \
  --global-arg region=us-east-1 \
  --global-arg cidrBlock=10.0.0.0/16 \
  --json
```

Dot notation creates nested objects:

```bash
--global-arg config.db.host=localhost --global-arg config.db.port=5432
# → globalArguments: { config: { db: { host: "localhost", port: "5432" } } }
```

**Output shape:**

```json
{
  "path": "definitions/command/shell/my-shell.yaml",
  "type": "command/shell",
  "name": "my-shell"
}
```

After creation, edit the YAML file to set per-method `arguments` in the
`methods` section.

**Example input file:**

```yaml
id: 550e8400-e29b-41d4-a716-446655440000
name: my-shell
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: "echo 'Hello, world!'"
```

### Definition-Level Check Selection

Definitions can control which pre-flight checks run via the `checks` field:

```yaml
id: 550e8400-e29b-41d4-a716-446655440000
name: my-vpc
version: 1
tags: {}
checks:
  require:
    - no-cidr-overlap # Must run, immune to --skip-checks CLI flags
  skip:
    - slow-api-check # Always skipped
globalArguments:
  cidrBlock: "10.0.0.0/16"
methods:
  create:
    arguments: {}
```

**Precedence rules:**

- `skip` always wins — even over `require` for the same check name
- `require` makes checks immune to `--skip-checks`, `--skip-check <name>`, and
  `--skip-check-label <label>` CLI flags (e.g., `--skip-checks` skips
  non-required checks but required checks still run)
- `require` checks still respect `appliesTo` method scoping
- `model validate` honors `skip` lists and warns on require/skip overlap;
  validation errors if a check name doesn't exist on the model type

### Model Inputs Schema

Models can define an `inputs` schema for runtime parameterization:

```yaml
id: 550e8400-e29b-41d4-a716-446655440000
name: my-deploy
version: 1
tags: {}
inputs:
  properties:
    environment:
      type: string
      enum: ["dev", "staging", "production"]
      description: Target environment
    dryRun:
      type: boolean
      default: false
  required: ["environment"]
globalArguments:
  target: ${{ inputs.environment }}
  simulate: ${{ inputs.dryRun }}
methods:
  deploy:
    arguments: {}
```

Inputs are provided at runtime with `--input` or `--input-file` and referenced
in globalArguments using `${{ inputs.<name> }}` expressions.

**Factory pattern:** Use inputs to create multiple instances from one model
definition — see
[references/scenarios.md#scenario-5](references/scenarios.md#scenario-5-factory-pattern-for-model-reuse).

## Edit a Model

**Recommended:** Use `swamp model get <name> --json` to get the file path, then
edit directly with the Edit tool, then validate with
`swamp model validate <name> --json`.

**Alternative methods:**

- Interactive: `swamp model edit my-shell` (opens in system editor)
- Stdin: `cat updated.yaml | swamp model edit my-shell --json`

Run `swamp repo index` if search results seem stale after editing.

## Delete a Model

Delete a model and all related artifacts (data, outputs, logs).

```bash
swamp model delete my-shell --json
```

**Output shape:**

```json
{
  "deleted": true,
  "modelId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "modelName": "my-shell",
  "artifactsDeleted": {
    "outputs": 5,
    "dataItems": 3
  }
}
```

## Validate Model Inputs

Validate a model definition against its type schema. Use `--label` to run only
checks with a specific label, and `--method` to simulate validation for a
specific method context.

```bash
swamp model validate my-shell --json
swamp model validate --json                          # Validate all models
swamp model validate my-shell --label policy --json  # Only checks with label "policy"
swamp model validate my-shell --method create --json # Validate for a specific method
```

**Output shape (single):**

```json
{
  "modelId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "modelName": "my-shell",
  "type": "command/shell",
  "validations": [
    { "name": "Definition schema validation", "passed": true },
    { "name": "Method arguments validation", "passed": true },
    { "name": "Expression syntax valid", "passed": true }
  ],
  "passed": true
}
```

**Output shape (all):**

```json
{
  "models": [
    { "modelId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "modelName": "my-shell", "validations": [...], "passed": true }
  ],
  "totalPassed": 5,
  "totalFailed": 1,
  "passed": false
}
```

## Expression Language

Model inputs support CEL expressions using `${{ <expression> }}` syntax.
Reference types, data versioning functions, and examples are in
[references/expressions.md](references/expressions.md).

## Evaluate Model Inputs

Evaluate expressions and write results to `inputs-evaluated/`.

```bash
swamp model evaluate my-subnet --json
swamp model evaluate --all --json
```

**Output shape:**

```json
{
  "evaluatedInputs": [
    {
      "name": "my-subnet",
      "type": "aws/subnet",
      "path": "inputs-evaluated/aws/subnet/my-subnet.yaml"
    }
  ]
}
```

## Run Methods

Execute a method on a model input.

```bash
swamp model method run my-shell execute --json
swamp model method run my-deploy create --input '{"environment": "prod"}' --json
swamp model method run my-deploy create --input-file inputs.yaml --json
swamp model method run my-deploy create --last-evaluated --json
swamp model method run my-deploy create --skip-checks --json
swamp model method run my-deploy create --skip-check valid-region --json
swamp model method run my-deploy create --skip-check-label live --json
```

Pre-flight checks run automatically before mutating methods (`create`, `update`,
`delete`, `action`). Read-only methods (`sync`, `get`, etc.) do not trigger
checks.

**Options:**

| Flag                         | Description                                      |
| ---------------------------- | ------------------------------------------------ |
| `--input <json>`             | Input values as JSON string                      |
| `--input-file <f>`           | Input values from YAML file                      |
| `--last-evaluated`           | Use previously evaluated model (skip eval)       |
| `--skip-checks`              | Skip all pre-flight checks                       |
| `--skip-check <name>`        | Skip a specific check by name (repeatable)       |
| `--skip-check-label <label>` | Skip all checks with a given label (repeatable)  |
| `--driver <driver>`          | Override execution driver (e.g. `raw`, `docker`) |

**Output shape:**

```json
{
  "outputId": "d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5",
  "modelId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "modelName": "my-shell",
  "method": "execute",
  "status": "succeeded",
  "duration": 150,
  "artifacts": {
    "resource": ".swamp/data/command/shell/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d/result/1/raw"
  }
}
```

## Model Outputs

### Search Outputs

Find method execution outputs.

```bash
swamp model output search --json
swamp model output search "my-shell" --json
```

**Output shape:**

```json
{
  "query": "",
  "results": [
    {
      "outputId": "d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5",
      "modelName": "my-shell",
      "method": "execute",
      "status": "succeeded",
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ]
}
```

### Get Output Details

Get full details of a specific output or latest output for a model.

```bash
swamp model output get d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5 --json
swamp model output get my-shell --json  # Latest output for model
```

**Output shape:**

```json
{
  "outputId": "d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5",
  "modelId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "modelName": "my-shell",
  "type": "command/shell",
  "method": "execute",
  "status": "succeeded",
  "startedAt": "2025-01-15T10:30:00Z",
  "completedAt": "2025-01-15T10:30:00.150Z",
  "artifacts": [
    { "type": "resource", "path": "..." }
  ]
}
```

### View Output Logs

Get log content from a method execution.

```bash
swamp model output logs d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5 --json
```

**Output shape:**

```json
{
  "outputId": "d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5",
  "logs": "Executing shell command...\nHello, world!\nCommand completed successfully."
}
```

### View Output Data

Get data artifact content from a method execution.

```bash
swamp model output data d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5 --json
```

**Output shape:**

```json
{
  "outputId": "d1e2f3a4-b5c6-4d7e-f8a9-b0c1d2e3f4a5",
  "data": {
    "exitCode": 0,
    "command": "echo 'Hello, world!'",
    "executedAt": "2025-01-15T10:30:00Z"
  }
}
```

## Workflow Example

1. **Search** for the right type: `swamp model type search "shell" --json`
2. **Describe** to understand the schema:
   `swamp model type describe command/shell --json`
3. **Create** an input file: `swamp model create command/shell my-shell --json`
4. **Edit** the YAML file to set `methods.execute.arguments.run`
5. **Validate** the model: `swamp model validate my-shell --json`
6. **Run** the method: `swamp model method run my-shell execute --json`
7. **View** the output: `swamp model output get my-shell --json`

## Data Ownership

Data is owned by the creating model — see
[references/data-ownership.md](references/data-ownership.md) for rules and
validation.

## When to Use Other Skills

| Need                            | Use Skill               |
| ------------------------------- | ----------------------- |
| Create/run workflows            | `swamp-workflow`        |
| Manage secrets                  | `swamp-vault`           |
| Repository structure            | `swamp-repo`            |
| Manage data lifecycle           | `swamp-data`            |
| Create custom TypeScript models | `swamp-extension-model` |

## References

- **Examples**: See [references/examples.md](references/examples.md) for
  complete model workflows and CEL expression reference
- **Troubleshooting**: See
  [references/troubleshooting.md](references/troubleshooting.md) for common
  errors and fixes
- **Scenarios**: See [references/scenarios.md](references/scenarios.md) for
  end-to-end scenarios (shell commands, chained lookups, runtime inputs)
- **Data chaining**: See
  [references/data-chaining.md](references/data-chaining.md) for command/shell
  model examples and chaining patterns
- **Execution drivers**: See
  [references/execution-drivers.md](references/execution-drivers.md)


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/expressions.md

```markdown
# Expression Language Reference

Model inputs support CEL expressions using `${{ <expression> }}` syntax.

## Reference Types

| Reference                                                               | Description                                             |
| ----------------------------------------------------------------------- | ------------------------------------------------------- |
| `inputs.<name>`                                                         | Runtime input value                                     |
| `model.<name>.input.globalArguments.<field>`                            | Another model's global argument                         |
| `model.<name>.resource.<specName>.<instanceName>.attributes.<field>`    | A model's resource data field (spec → instance → field) |
| `model.<name>.file.<specName>.<instanceName>.{path\|size\|contentType}` | A model's file metadata (spec → instance → field)       |
| `file.contents("<modelName>", "<specName>")`                            | Lazy-load file contents from disk                       |
| `self.name`                                                             | This model's name                                       |
| `self.version`                                                          | This model's version                                    |
| `self.globalArguments.<field>`                                          | This model's own global argument                        |

> **Instance name convention:** For single-instance resources (most models),
> `instanceName` equals `specName`. For example, a model `my-shell` with
> resource spec `result` is accessed as
> `model.my-shell.resource.result.result.attributes.exitCode`. Factory models
> use distinct instance names — see the `swamp-extension-model` skill for
> details.

## CEL Operations

- **String concatenation:** `self.name + "-suffix"`
- **Arithmetic:** `self.globalArguments.count * 2`
- **Conditionals:** `self.globalArguments.enabled ? "yes" : "no"`

## Data Versioning Functions

Access specific versions of model data using the `data` namespace:

| Function                                     | Description                               |
| -------------------------------------------- | ----------------------------------------- |
| `data.version(modelName, dataName, version)` | Get specific version of data              |
| `data.latest(modelName, dataName)`           | Get latest version of data                |
| `data.listVersions(modelName, dataName)`     | Get array of available version numbers    |
| `data.findByTag(tagKey, tagValue)`           | Find all data matching a tag              |
| `data.findBySpec(modelName, specName)`       | Find all data from a specific output spec |

**DataRecord structure** returned by these functions:

```json
{
  "id": "uuid",
  "name": "data-name",
  "version": 3,
  "createdAt": "2025-01-15T10:30:00Z",
  "attributes": {/* data content */},
  "tags": { "type": "resource" }
}
```

**Example usage:**

```yaml
# Get specific version
oldValue: ${{ data.version("my-model", "state", 2).attributes.value }}

# Get latest
current: ${{ data.latest("my-model", "output").attributes.result }}

# List versions for conditional logic
hasHistory: ${{ size(data.listVersions("my-model", "state")) > 1 }}

# Find by tag
allResources: ${{ data.findByTag("type", "resource") }}

# Find all instances from a factory model's output spec
subnets: ${{ data.findBySpec("my-scanner", "subnet") }}
```

## Example with Expressions

```yaml
id: 550e8400-e29b-41d4-a716-446655440001
name: my-subnet
version: 1
tags: {}
globalArguments:
  vpcId: ${{ model.my-vpc.resource.state.current.attributes.VpcId }}
  cidrBlock: "10.0.1.0/24"
  tags:
    Name: ${{ self.name + "-subnet" }}
methods:
  create:
    arguments: {}
```

```

### references/data-ownership.md

```markdown
# Data Ownership

Data artifacts are owned by the model (definition) that created them. This
prevents accidental overwrites from other models.

## Ownership Rules

- Data can only be written by the model that originally created it
- Ownership is verified through `definitionHash` comparison
- Owner information includes:
  - **type**: `model-method`, `workflow-step`, or `manual`
  - **ref**: Reference to the creating entity
  - **workflow/run IDs**: Set when created during workflow execution

## Ownership Validation

When a model method writes data, swamp validates:

1. If data doesn't exist → create with current model as owner
2. If data exists → verify `ownerDefinition.definitionHash` matches
3. If hash mismatch → write fails with ownership error

This ensures data integrity when multiple models reference the same data names.

```

### references/examples.md

```markdown
# Model Examples and CEL Reference

## Table of Contents

- [CEL Expression Quick Reference](#cel-expression-quick-reference)
- [Decision Tree: What to Build](#decision-tree-what-to-build)
- [Simple Shell Command Model](#simple-shell-command-model)
- [Chained Lookup Models](#chained-lookup-models)
- [Model with Runtime Inputs](#model-with-runtime-inputs)
- [Cross-Model Data References](#cross-model-data-references)

## CEL Expression Quick Reference

| Expression Pattern                                           | Description                               | Example Value                 |
| ------------------------------------------------------------ | ----------------------------------------- | ----------------------------- |
| `data.latest("<model>", "<name>").attributes.<field>`        | Latest data (PREFERRED, sync disk read)   | VPC ID, subnet CIDR, etc.     |
| `data.version("<model>", "<name>", N).attributes.<field>`    | Specific version of data                  | Rollback to version 1         |
| `data.findBySpec("<model>", "<spec>")`                       | Find all instances from a spec            | All subnets from scanner      |
| `data.findByTag("<key>", "<value>")`                         | Find data by tag                          | All resources tagged env=prod |
| `model.<name>.resource.<spec>.<instance>.attributes.<field>` | Cross-model resource (DEPRECATED)         | VPC ID, subnet CIDR, etc.     |
| `model.<name>.resource.result.result.attributes.stdout`      | command/shell stdout (DEPRECATED)         | AMI ID from aws cli command   |
| `model.<name>.file.<spec>.<instance>.path`                   | File path from another model (DEPRECATED) | `/path/to/file.txt`           |
| `self.name`                                                  | Current model's name                      | `my-vpc`                      |
| `self.version`                                               | Current model's version                   | `1`                           |
| `self.globalArguments.<field>`                               | This model's own global argument          | CIDR block, region, etc.      |
| `inputs.<name>`                                              | Runtime input value                       | `production`, `true`, etc.    |
| `env.<VAR_NAME>`                                             | Environment variable                      | AWS region, credentials       |
| `vault.get("<vault>", "<key>")`                              | Vault secret                              | API key, password             |

### CEL Path Patterns by Model Type

| Model Type      | Resource Spec | Instance    | CEL Path Example                                              |
| --------------- | ------------- | ----------- | ------------------------------------------------------------- |
| `command/shell` | `result`      | `result`    | `model.my-shell.resource.result.result.attributes.stdout`     |
| `@user/vpc`     | `vpc`         | `main`      | `model.my-vpc.resource.vpc.main.attributes.VpcId`             |
| `@user/subnet`  | `subnet`      | `primary`   | `model.my-subnet.resource.subnet.primary.attributes.SubnetId` |
| Factory model   | `<spec>`      | `<dynamic>` | `model.scanner.resource.subnet.subnet-aaa.attributes.cidr`    |

## Decision Tree: What to Build

```
What does the user want to accomplish?
│
├── Run a single command or API call
│   └── Create a swamp model (command/shell or @user/custom)
│
├── Orchestrate multiple steps in order
│   └── Create a swamp workflow with jobs and steps
│
├── Need custom capabilities not in existing types
│   └── Create an extension model (@user/my-type) in extensions/models/
│
└── Combine all of the above
    └── Create extension models + workflows that use them
```

## Simple Shell Command Model

**Step 1: Create the model**

```bash
swamp model create command/shell my-shell --json
```

**Step 2: Configure the model input**

```yaml
# models/my-shell/input.yaml
name: my-shell
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: "echo 'Hello from ${{ self.name }}'"
```

**Step 3: Run and access output**

```bash
swamp model method run my-shell execute --json
```

**Output data path**: `model.my-shell.resource.result.result.attributes.stdout`

## Chained Lookup Models

### Pattern: VPC → Subnet → Instance

**Step 1: VPC Lookup**

```bash
swamp model create command/shell vpc-lookup --json
```

```yaml
# models/vpc-lookup/input.yaml
name: vpc-lookup
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-vpcs --filters "Name=isDefault,Values=true"
        --query "Vpcs[0].VpcId" --output text
```

```bash
swamp model method run vpc-lookup execute --json
```

**Step 2: Subnet Lookup (references VPC)**

```bash
swamp model create command/shell subnet-lookup --json
```

```yaml
# models/subnet-lookup/input.yaml
name: subnet-lookup
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-subnets
        --filters "Name=vpc-id,Values=${{ model.vpc-lookup.resource.result.result.attributes.stdout }}"
        --query "Subnets[0].SubnetId" --output text
```

**Step 3: Instance (references both)**

```bash
swamp model create @user/ec2-instance my-instance --json
```

```yaml
# models/my-instance/input.yaml
name: my-instance
version: 1
tags: {}
globalArguments:
  vpcId: ${{ model.vpc-lookup.resource.result.result.attributes.stdout }}
  subnetId: ${{ model.subnet-lookup.resource.result.result.attributes.stdout }}
  instanceType: t3.micro
  tags:
    Name: ${{ self.name }}
```

### Key CEL Paths Used

| Model         | Expression                                                     | Value             |
| ------------- | -------------------------------------------------------------- | ----------------- |
| vpc-lookup    | `model.vpc-lookup.resource.result.result.attributes.stdout`    | `vpc-12345678`    |
| subnet-lookup | `model.subnet-lookup.resource.result.result.attributes.stdout` | `subnet-abcd1234` |
| my-instance   | `self.name`                                                    | `my-instance`     |

## Model with Runtime Inputs

Models can accept runtime inputs via `--input` or `--input-file`:

**Step 1: Define model with inputs schema**

```yaml
# models/my-deploy/input.yaml
name: my-deploy
version: 1
tags: {}
inputs:
  properties:
    environment:
      type: string
      enum: ["dev", "staging", "production"]
      description: Target environment
    dryRun:
      type: boolean
      default: false
  required: ["environment"]
globalArguments:
  target: ${{ inputs.environment }}
  simulate: ${{ inputs.dryRun }}
methods:
  deploy:
    arguments: {}
```

**Step 2: Run with inputs**

```bash
# JSON input
swamp model method run my-deploy deploy --input '{"environment": "production"}' --json

# YAML file input
swamp model method run my-deploy deploy --input-file inputs.yaml --json
```

**Input file format (inputs.yaml)**:

```yaml
environment: production
dryRun: true
```

## Cross-Model Data References

### Preferred: data.latest() Expressions

Always use `data.latest()` for referencing other models' data. It reads directly
from disk on every call, so it always reflects the latest state:

```yaml
# CORRECT: data.latest() — always reads fresh data from disk
globalArguments:
  vpcId: ${{ data.latest("my-vpc", "main").attributes.VpcId }}

# DEPRECATED: model.*.resource — will be removed in a future release
globalArguments:
  vpcId: ${{ model.my-vpc.resource.vpc.main.attributes.VpcId }}
```

### Why data.latest() is Preferred

| Feature                   | `data.latest()` | `model.*.resource` |
| ------------------------- | --------------- | ------------------ |
| Always fresh (no cache)   | Yes (sync disk) | Yes (eager load)   |
| Supports vary dimensions  | Yes             | No                 |
| Clear dependency tracking | Yes             | Yes                |
| Future-proof              | Yes (canonical) | No (deprecated)    |

### Other data.* Functions

```yaml
# Specific version (rollback scenario)
previousConfig: ${{ data.version("app-config", "config", 1).attributes.setting }}

# Dynamic model name
dynamicValue: ${{ data.latest(inputs.modelName, "state").attributes.value }}

# Find all instances of a spec
allSubnets: ${{ data.findBySpec("scanner", "subnet") }}

# Find by tag
prodResources: ${{ data.findByTag("env", "prod") }}
```

### Self-References

Use `self.*` to reference the current model's properties:

```yaml
globalArguments:
  resourceName: ${{ self.name }}-resource
  version: ${{ self.version }}
  existingCidr: ${{ self.globalArguments.cidrBlock }}
```

### Environment Variables

```yaml
globalArguments:
  region: ${{ env.AWS_REGION }}
  profile: ${{ env.AWS_PROFILE }}
```

### Vault Secrets

```yaml
globalArguments:
  apiKey: ${{ vault.get("prod-vault", "API_KEY") }}
  dbPassword: ${{ vault.get("prod-vault", "DB_PASSWORD") }}
```

```

### references/troubleshooting.md

```markdown
# Model Troubleshooting

## Table of Contents

- [Common Errors](#common-errors)
  - ["Model not found"](#model-not-found)
  - ["Method not found"](#method-not-found)
  - ["Validation failed"](#validation-failed)
  - ["Expression evaluation failed"](#expression-evaluation-failed)
  - ["No such key" in CEL expressions](#no-such-key-in-cel-expressions)
  - ["Model type not found"](#model-type-not-found)
- [Expression Debugging](#expression-debugging)
- [Method Execution Issues](#method-execution-issues)
- [Data and Output Issues](#data-and-output-issues)

## Common Errors

### "Model not found"

**Symptom**: `Error: Model 'my-model' not found`

**Causes and solutions**:

1. **Typo in model name** — list available models:

   ```bash
   swamp model search --json
   ```

2. **Model not created** — create it:

   ```bash
   swamp model create <type> my-model --json
   ```

3. **Using ID instead of name** — both work, but verify:

   ```bash
   swamp model get my-model --json
   # Check both id and name fields
   ```

### "Method not found"

**Symptom**: `Error: Method 'deploy' not found on model type`

**Causes and solutions**:

1. **Wrong method name** — check available methods:

   ```bash
   swamp model type describe <type> --json
   # Look at methods array
   ```

2. **Method not configured in input** — add to model input:

   ```yaml
   methods:
     deploy:
       arguments: {}
   ```

### "Validation failed"

**Symptom**: `swamp model validate` returns errors

**Common validation errors**:

| Error                      | Solution                              |
| -------------------------- | ------------------------------------- |
| Missing required field     | Add the field to model input          |
| Invalid type for field     | Check schema, fix value type          |
| Unknown property           | Remove field or check for typo        |
| Invalid expression syntax  | Fix CEL expression syntax             |
| Referenced model not found | Create the model or fix the reference |

**Debug steps**:

```bash
# 1. Get the type schema
swamp model type describe <type> --json

# 2. Compare with your input
swamp model get my-model --json

# 3. Validate with verbose output
swamp model validate my-model --json
```

### "Expression evaluation failed"

**Symptom**: `Error evaluating expression: <expression>`

**Common causes**:

1. **Referenced model has no data**:

   ```bash
   # Check if the model has run
   swamp data list <referenced-model> --json
   ```

2. **Wrong attribute path**:

   ```bash
   # Get the actual data structure
   swamp data get <model> <data-name> --json
   # Check the attributes object
   ```

3. **Syntax error in expression**:

   ```yaml
   # Wrong — missing instance name
   value: ${{ model.my-vpc.resource.vpc.attributes.VpcId }}

   # Correct — spec="vpc", instance="main"
   value: ${{ model.my-vpc.resource.vpc.main.attributes.VpcId }}
   ```

### "No such key" in CEL expressions

**Symptom**: `Error: No such key: <keyname>`

**Causes**:

1. **Missing instance name in path**:

   ```yaml
   # Wrong — missing instance name
   vpcId: ${{ model.my-vpc.resource.vpc.attributes.VpcId }}

   # Correct — include instance name (spec="vpc", instance="main")
   vpcId: ${{ model.my-vpc.resource.vpc.main.attributes.VpcId }}
   ```

2. **Model never executed** — expressions referencing `model.*.resource` or
   `model.*.file` are automatically skipped when the referenced model has no
   data. If a method accesses a skipped field, it throws a clear error:

   ```
   Unresolved expression in globalArguments.ssh_keys: ${{ model.ssh-key.resource... }}
   ```

   To fix, run the referenced model first:

   ```bash
   swamp model method run my-vpc create --json
   ```

3. **Hyphen in spec name** (CEL interprets as subtraction):

   ```yaml
   # Wrong — spec name with hyphen
   resource.internet-gateway.internet-gateway  # Parsed as subtraction

   # Correct — use camelCase or no hyphen
   resource.igw.igw
   ```

4. **Wrong attribute name**:

   ```bash
   # Check actual attribute names
   swamp data get my-vpc vpc --json
   ```

### "Unresolved expression in globalArguments"

**Symptom**:
`Error: Unresolved expression in globalArguments.<field>: ${{ ... }}`

**Cause**: A `globalArguments` field contains a CEL expression that couldn't be
resolved (e.g., the referenced model has no resource data), and the method tried
to use that field.

**Solutions**:

1. **Run the referenced model first** so its data is available:

   ```bash
   swamp model method run <referenced-model> create --json
   ```

2. **Use a workflow** that runs models in the correct order — dependencies are
   resolved automatically within a workflow run.

### "Model type not found"

**Symptom**: `Error: Model type '<type>' not found`

**Causes and solutions**:

1. **Typo in type name**:

   ```bash
   swamp model type search --json
   ```

2. **Extension model not loaded** — check for syntax errors:

   ```bash
   # Look for error messages at startup
   swamp model type search 2>&1 | grep -i error
   ```

3. **Extension in wrong directory**:

   ```
   extensions/models/my_model.ts  # Correct
   extensions/my_model.ts         # Wrong
   models/my_model.ts             # Wrong
   ```

## Expression Debugging

### Step-by-Step Debug Process

**Step 1: Validate the expression references a real model**

```bash
swamp model get <referenced-model> --json
```

**Step 2: Verify the model has data**

```bash
swamp data list <referenced-model> --json
```

**Step 3: Check the exact data structure**

```bash
swamp data get <referenced-model> <data-name> --json
```

**Step 4: Build the expression path**

```
model.<model-name>.resource.<specName>.<instanceName>.attributes.<field>

Example:
model.my-vpc.resource.vpc.main.attributes.VpcId
      └─────┘         └─┘ └──┘            └───┘
      model name      |    |              attribute
                      |    └── instanceName (from writeResource 2nd arg)
                      └── specName (from writeResource 1st arg)
```

### Common Expression Patterns

| Model Type    | Spec     | Instance | Expression Pattern                                           |
| ------------- | -------- | -------- | ------------------------------------------------------------ |
| command/shell | result   | result   | `model.<name>.resource.result.result.attributes.stdout`      |
| Custom model  | (varies) | (varies) | `model.<name>.resource.<spec>.<instance>.attributes.<field>` |

## Method Execution Issues

### Method Runs But No Output

**Symptom**: Method succeeds but no data artifacts

**Causes**:

1. **Model doesn't call writeResource**:

   ```typescript
   // Must return dataHandles
   return { dataHandles: [handle] };
   ```

2. **Method threw after writing** — check logs:

   ```bash
   swamp model output logs <output-id> --json
   ```

### Method Fails with Timeout

**Symptom**: Method times out

**Solutions**:

1. **Increase timeout for long-running operations**

2. **Check for infinite loops in custom models**

3. **Verify network connectivity for API calls**

### Method Fails with Authentication Error

**Symptom**: AWS/API authentication errors

**Solutions**:

```bash
# Check environment variables
echo $AWS_ACCESS_KEY_ID
echo $AWS_REGION

# Verify credentials work
aws sts get-caller-identity

# Check vault expressions resolve
swamp vault get <vault-name> <key> --json
```

## Data and Output Issues

### Output Shows "succeeded" But Data Missing

**Debug steps**:

```bash
# 1. Get output details
swamp model output get <model-name> --json

# 2. Check artifacts array
# Look for "artifacts" in output

# 3. List model data
swamp data list <model-name> --json

# 4. Rebuild index if needed
swamp repo index --json
```

### Cannot Find Previous Output

```bash
# Search all outputs
swamp model output search --json

# Search by model name
swamp model output search "my-model" --json

# Get specific output
swamp model output get <output-id> --json
```

### Data Versions Not Visible

```bash
# List all versions
swamp data versions <model-name> <data-name> --json

# Check GC settings haven't pruned them
swamp data get <model-name> <data-name> --json
# Look at gcSetting field
```

```

### references/scenarios.md

```markdown
# Model Scenarios

End-to-end scenarios showing how to build models for common use cases.

## Table of Contents

- [Scenario 1: Simple Shell Command](#scenario-1-simple-shell-command)
- [Scenario 2: Chained AWS Lookups](#scenario-2-chained-aws-lookups)
- [Scenario 3: Model with Runtime Inputs](#scenario-3-model-with-runtime-inputs)
- [Scenario 4: Multi-Environment Configuration](#scenario-4-multi-environment-configuration)
- [Scenario 5: Factory Pattern for Model Reuse](#scenario-5-factory-pattern-for-model-reuse)

---

## Scenario 1: Simple Shell Command

### User Request

> "I want to run a shell command and capture its output for use in other
> models."

### What You'll Build

- 1 model: `command/shell` type

### Decision Tree

```
User wants to run a command → Use command/shell model
```

### Step-by-Step

**1. Create the model**

```bash
swamp model create command/shell my-shell --json
```

**2. Configure the model input**

```bash
swamp model get my-shell --json
# Note the path, then edit the file
```

Edit `models/my-shell/input.yaml`:

```yaml
name: my-shell
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: "uname -a"
```

**3. Validate**

```bash
swamp model validate my-shell --json
```

**4. Run**

```bash
swamp model method run my-shell execute --json
```

**5. View output**

```bash
swamp model output get my-shell --json
swamp data get my-shell result --json
```

### CEL Paths Used

| Field    | CEL Path                                                    |
| -------- | ----------------------------------------------------------- |
| stdout   | `model.my-shell.resource.result.result.attributes.stdout`   |
| stderr   | `model.my-shell.resource.result.result.attributes.stderr`   |
| exitCode | `model.my-shell.resource.result.result.attributes.exitCode` |

---

## Scenario 2: Chained AWS Lookups

### User Request

> "I need to look up my default VPC, find a subnet in it, and then create an EC2
> instance using that subnet."

### What You'll Build

- 3 models:
  - `vpc-lookup` (command/shell) — find the default VPC
  - `subnet-lookup` (command/shell) — find a subnet in that VPC
  - `my-instance` (@user/ec2-instance or similar) — uses both

### Decision Tree

```
User wants to chain multiple lookups → Multiple models with CEL references
Each lookup is a command → command/shell model
Final resource needs custom logic → Extension model (or use existing type)
```

### Step-by-Step

**1. Create VPC lookup model**

```bash
swamp model create command/shell vpc-lookup --json
```

Edit `models/vpc-lookup/input.yaml`:

```yaml
name: vpc-lookup
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-vpcs
        --filters "Name=isDefault,Values=true"
        --query "Vpcs[0].VpcId" --output text
```

Run it:

```bash
swamp model method run vpc-lookup execute --json
```

**2. Create subnet lookup model (references VPC)**

```bash
swamp model create command/shell subnet-lookup --json
```

Edit `models/subnet-lookup/input.yaml`:

```yaml
name: subnet-lookup
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-subnets
        --filters "Name=vpc-id,Values=${{ model.vpc-lookup.resource.result.result.attributes.stdout }}"
        --query "Subnets[0].SubnetId" --output text
```

Validate and run:

```bash
swamp model validate subnet-lookup --json
swamp model method run subnet-lookup execute --json
```

**3. Create instance model (references both)**

```bash
swamp model create @user/ec2-instance my-instance --json
```

Edit `models/my-instance/input.yaml`:

```yaml
name: my-instance
version: 1
tags: {}
globalArguments:
  vpcId: ${{ model.vpc-lookup.resource.result.result.attributes.stdout }}
  subnetId: ${{ model.subnet-lookup.resource.result.result.attributes.stdout }}
  instanceType: t3.micro
  tags:
    Name: ${{ self.name }}
    Environment: dev
```

Validate:

```bash
swamp model validate my-instance --json
```

### CEL Paths Used

| Model         | Expression                                                     | Description |
| ------------- | -------------------------------------------------------------- | ----------- |
| vpc-lookup    | `model.vpc-lookup.resource.result.result.attributes.stdout`    | VPC ID      |
| subnet-lookup | `model.subnet-lookup.resource.result.result.attributes.stdout` | Subnet ID   |
| my-instance   | `self.name`                                                    | Model name  |

---

## Scenario 3: Model with Runtime Inputs

### User Request

> "I want a deployment model where I can specify the environment (dev, staging,
> prod) at runtime instead of hardcoding it."

### What You'll Build

- 1 model with `inputs` schema

### Decision Tree

```
User wants runtime parameterization → Use inputs schema
Values change per invocation → --input or --input-file
```

### Step-by-Step

**1. Create the model**

```bash
swamp model create @user/deployment my-deploy --json
```

**2. Configure with inputs schema**

Edit `models/my-deploy/input.yaml`:

```yaml
name: my-deploy
version: 1
tags: {}
inputs:
  properties:
    environment:
      type: string
      enum: ["dev", "staging", "production"]
      description: Target deployment environment
    replicas:
      type: integer
      default: 1
      minimum: 1
      maximum: 10
    dryRun:
      type: boolean
      default: false
  required: ["environment"]
globalArguments:
  target: ${{ inputs.environment }}
  instanceCount: ${{ inputs.replicas }}
  simulate: ${{ inputs.dryRun }}
methods:
  deploy:
    arguments: {}
```

**3. Validate**

```bash
swamp model validate my-deploy --json
```

**4. Run with different inputs**

```bash
# Dev environment
swamp model method run my-deploy deploy --input '{"environment": "dev"}' --json

# Production with 3 replicas
swamp model method run my-deploy deploy --input '{"environment": "production", "replicas": 3}' --json

# Staging dry run
swamp model method run my-deploy deploy --input '{"environment": "staging", "dryRun": true}' --json
```

**5. Alternative: Use input file**

Create `inputs/production.yaml`:

```yaml
environment: production
replicas: 5
dryRun: false
```

Run with file:

```bash
swamp model method run my-deploy deploy --input-file inputs/production.yaml --json
```

### CEL Paths Used

| Field       | CEL Path             | Runtime Value                        |
| ----------- | -------------------- | ------------------------------------ |
| environment | `inputs.environment` | `"dev"`, `"staging"`, `"production"` |
| replicas    | `inputs.replicas`    | `1`, `3`, `5`, etc.                  |
| dryRun      | `inputs.dryRun`      | `true`, `false`                      |

---

## Scenario 4: Multi-Environment Configuration

### User Request

> "I want to deploy to multiple environments with different configurations. Each
> environment should use its own vault for secrets."

### What You'll Build

- 3 vaults: `dev-secrets`, `staging-secrets`, `prod-secrets`
- 1 model with environment-aware vault expressions

### Decision Tree

```
Different secrets per environment → Multiple vaults
Single model definition → CEL expressions select vault dynamically
```

### Step-by-Step

**1. Create vaults for each environment**

```bash
swamp vault create local_encryption dev-secrets --json
swamp vault create local_encryption staging-secrets --json
swamp vault create aws prod-secrets --json  # Production uses AWS
```

**2. Store secrets in each vault**

```bash
swamp vault put dev-secrets API_KEY=dev-key-12345 --json
swamp vault put staging-secrets API_KEY=staging-key-67890 --json
swamp vault put prod-secrets API_KEY=prod-key-secure --json
```

**3. Create model with conditional vault access**

Since CEL doesn't support dynamic vault names directly, create separate model
instances per environment:

```yaml
# models/api-client-dev/input.yaml
name: api-client-dev
version: 1
tags:
  environment: dev
globalArguments:
  apiKey: ${{ vault.get("dev-secrets", "API_KEY") }}
  endpoint: https://api.dev.example.com
```

```yaml
# models/api-client-staging/input.yaml
name: api-client-staging
version: 1
tags:
  environment: staging
globalArguments:
  apiKey: ${{ vault.get("staging-secrets", "API_KEY") }}
  endpoint: https://api.staging.example.com
```

```yaml
# models/api-client-prod/input.yaml
name: api-client-prod
version: 1
tags:
  environment: production
globalArguments:
  apiKey: ${{ vault.get("prod-secrets", "API_KEY") }}
  endpoint: https://api.example.com
```

**4. Create a workflow that selects the right model**

```yaml
# workflows/deploy-api/workflow.yaml
name: deploy-api
version: 1
inputs:
  properties:
    environment:
      type: string
      enum: ["dev", "staging", "production"]
  required: ["environment"]
jobs:
  - name: deploy
    steps:
      - name: deploy-dev
        condition: ${{ inputs.environment == "dev" }}
        task:
          type: model_method
          modelIdOrName: api-client-dev
          methodName: deploy
      - name: deploy-staging
        condition: ${{ inputs.environment == "staging" }}
        task:
          type: model_method
          modelIdOrName: api-client-staging
          methodName: deploy
      - name: deploy-prod
        condition: ${{ inputs.environment == "production" }}
        task:
          type: model_method
          modelIdOrName: api-client-prod
          methodName: deploy
```

**5. Run for each environment**

```bash
# Deploy to dev
swamp workflow run deploy-api --input '{"environment": "dev"}' --json

# Deploy to production
swamp workflow run deploy-api --input '{"environment": "production"}' --json
```

### CEL Paths Used

| Model              | Expression                                | Value               |
| ------------------ | ----------------------------------------- | ------------------- |
| api-client-dev     | `vault.get("dev-secrets", "API_KEY")`     | `dev-key-12345`     |
| api-client-staging | `vault.get("staging-secrets", "API_KEY")` | `staging-key-67890` |
| api-client-prod    | `vault.get("prod-secrets", "API_KEY")`    | `prod-key-secure`   |
| All models         | `self.name`                               | Model name          |

---

## Scenario 5: Factory Pattern for Model Reuse

### User Request

> "I need to create 4 subnets (public-a, public-b, private-a, private-b) but
> they all have the same schema. I don't want to maintain 4 separate model
> definitions."

### What You'll Build

- 1 model definition (`prod-subnet`) called 4 times with different inputs
- 4 distinct data instances keyed by `instanceName`

### Decision Tree

```
Multiple instances of the same resource type? → Factory pattern
  Same schema, different parameters? → Yes → One model + inputs
  Different schemas or behaviors? → No → Separate models
```

### When to Use Factory vs Separate Models

| Situation                                     | Approach          |
| --------------------------------------------- | ----------------- |
| 4 subnets with different CIDRs/AZs            | Factory (1 model) |
| 2 EIPs with different tags                    | Factory (1 model) |
| A VPC and a subnet (different resource types) | Separate models   |
| Resources with different method signatures    | Separate models   |

### Step-by-Step

**1. Create the model**

```bash
swamp model create @user/aws-subnet prod-subnet --json
```

**2. Configure with inputs schema**

Edit `models/prod-subnet/input.yaml`:

```yaml
name: prod-subnet
version: 1
tags: {}
inputs:
  properties:
    instanceName:
      type: string
      description: Unique name for this subnet instance (becomes the data name)
    cidrBlock:
      type: string
      description: CIDR block for the subnet
    availabilityZone:
      type: string
      description: AWS availability zone
  required: ["instanceName", "cidrBlock", "availabilityZone"]
globalArguments:
  name: ${{ inputs.instanceName }}
  VpcId: ${{ data.latest("prod-vpc", "main").attributes.VpcId }}
  CidrBlock: ${{ inputs.cidrBlock }}
  AvailabilityZone: ${{ inputs.availabilityZone }}
  Tags:
    - Key: Name
      Value: ${{ inputs.instanceName }}
methods:
  create:
    arguments: {}
  delete:
    arguments: {}
```

**3. The `name` and data name connection**

The `name: ${{ inputs.instanceName }}` in globalArguments is critical. It sets
the **data instance name**, so when you call the model with
`instanceName: "public-a"`, the output data is stored as `public-a`. This means
downstream models can access it with:

```yaml
subnetId: ${{ data.latest("prod-subnet", "public-a").attributes.SubnetId }}
```

Each call with a different `instanceName` creates a separate data instance under
the same model definition.

**4. Create workflow — call the model multiple times**

```yaml
name: create-subnets
version: 1
jobs:
  - name: create-subnets
    description: Create all 4 subnets in parallel
    steps:
      - name: create-public-a
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: create
          inputs:
            instanceName: public-a
            cidrBlock: "10.0.1.0/24"
            availabilityZone: us-east-1a
      - name: create-public-b
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: create
          inputs:
            instanceName: public-b
            cidrBlock: "10.0.2.0/24"
            availabilityZone: us-east-1b
      - name: create-private-a
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: create
          inputs:
            instanceName: private-a
            cidrBlock: "10.0.3.0/24"
            availabilityZone: us-east-1a
      - name: create-private-b
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: create
          inputs:
            instanceName: private-b
            cidrBlock: "10.0.4.0/24"
            availabilityZone: us-east-1b
```

Steps within a job run in parallel, so all 4 subnets are created concurrently.

**5. Delete workflow — provide inputs the method actually uses**

Delete steps must provide `instanceName` because
`name: ${{ inputs.instanceName }}` determines which data instance to read and
delete. Other inputs are only needed if the delete method implementation
accesses those globalArguments.

The system **selectively evaluates** globalArgument expressions — inputs that
aren't provided are skipped. A runtime error only occurs if the method code
actually tries to access an unresolved globalArgument.

```yaml
name: delete-subnets
version: 1
jobs:
  - name: delete-subnets
    description: Delete all 4 subnets
    steps:
      - name: delete-public-a
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: delete
          inputs:
            instanceName: public-a
            cidrBlock: "10.0.1.0/24"
            availabilityZone: us-east-1a
            identifier: ${{ data.latest("prod-subnet", "public-a").attributes.SubnetId }}
      - name: delete-public-b
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: delete
          inputs:
            instanceName: public-b
            cidrBlock: "10.0.2.0/24"
            availabilityZone: us-east-1b
            identifier: ${{ data.latest("prod-subnet", "public-b").attributes.SubnetId }}
      - name: delete-private-a
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: delete
          inputs:
            instanceName: private-a
            cidrBlock: "10.0.3.0/24"
            availabilityZone: us-east-1a
            identifier: ${{ data.latest("prod-subnet", "private-a").attributes.SubnetId }}
      - name: delete-private-b
        task:
          type: model_method
          modelIdOrName: prod-subnet
          methodName: delete
          inputs:
            instanceName: private-b
            cidrBlock: "10.0.4.0/24"
            availabilityZone: us-east-1b
            identifier: ${{ data.latest("prod-subnet", "private-b").attributes.SubnetId }}
```

### Understanding Input Requirements for Delete

The system handles unresolved globalArguments gracefully:

| Input              | Needed for delete?         | Why                                            |
| ------------------ | -------------------------- | ---------------------------------------------- |
| `instanceName`     | **Always**                 | Keys the data instance (`name` globalArgument) |
| `identifier`       | **Always**                 | The resource ID to delete                      |
| `cidrBlock`        | Only if method accesses it | Skipped if not provided and not used by method |
| `availabilityZone` | Only if method accesses it | Skipped if not provided and not used by method |

If your delete method implementation only reads `globalArgs.name` and
`args.identifier`, you can omit `cidrBlock` and `availabilityZone` from the
delete step inputs. Unresolved expressions are skipped — the system only throws
an error if the method code actually tries to access an unresolved value.

### CEL Paths Used

| Data                  | CEL Path                                                      |
| --------------------- | ------------------------------------------------------------- |
| Subnet ID (public-a)  | `data.latest("prod-subnet", "public-a").attributes.SubnetId`  |
| Subnet ID (private-b) | `data.latest("prod-subnet", "private-b").attributes.SubnetId` |
| VPC ID (dependency)   | `data.latest("prod-vpc", "main").attributes.VpcId`            |

```

### references/data-chaining.md

```markdown
# Data Chaining with command/shell Model

The `command/shell` model enables data chaining by running shell commands and
capturing output for use in other models. For JSON output, use `jq` in the
command to extract specific fields, then access the result via
`data.latest("model-name", "result").attributes.stdout`.

> **Note:** `data.latest()` is the preferred accessor for all cross-model data.
> The `model.*.resource` pattern is deprecated and will be removed in a future
> release. Existing examples below show both patterns for reference.

## command/shell Data Attributes

| Attribute    | Description                                   |
| ------------ | --------------------------------------------- |
| `stdout`     | Raw stdout from the command                   |
| `stderr`     | Raw stderr from the command                   |
| `exitCode`   | Command exit code                             |
| `executedAt` | ISO timestamp when command was executed       |
| `durationMs` | Duration of command execution in milliseconds |

## Example: Dynamic AMI Lookup

**Step 1: Create a command/shell model to look up an AMI:**

```bash
# Create the model
swamp model create command/shell latest-ami --json
```

Edit `models/latest-ami/input.yaml`:

```yaml
name: latest-ami
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-images --owners amazon
        --filters "Name=name,Values=amzn2-ami-hvm-*-x86_64-gp2"
        --query "sort_by(Images,&CreationDate)[-1].ImageId"
        --output text
```

**Step 2: Create another model that references the shell output:**

```bash
# Create the EC2 instance model
swamp model create @user/ec2-instance my-instance --json
```

Edit `models/my-instance/input.yaml`:

```yaml
name: my-instance
version: 1
tags: {}
globalArguments:
  # Reference stdout from the command/shell model
  imageId: ${{ model.latest-ami.resource.result.result.attributes.stdout }}
  instanceType: t3.micro
  tags:
    Name: ${{ self.name }}
```

## Example: Security Group Lookup

```bash
# Create and configure the security group lookup model
swamp model create command/shell default-sg --json
```

Edit `models/default-sg/input.yaml`:

```yaml
name: default-sg
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-security-groups
        --filters "Name=group-name,Values=default"
        --query "SecurityGroups[0].GroupId"
        --output text
```

```bash
# Create an EC2 instance that references the security group
swamp model create @user/ec2 my-server --json
```

Edit `models/my-server/input.yaml`:

```yaml
name: my-server
version: 1
tags: {}
globalArguments:
  securityGroupIds:
    - ${{ model.default-sg.resource.result.result.attributes.stdout }}
```

## Example: Chaining Multiple Lookups

```bash
# Step 1: Create VPC lookup model
swamp model create command/shell vpc-lookup --json
```

Edit `models/vpc-lookup/input.yaml`:

```yaml
name: vpc-lookup
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-vpcs --filters "Name=isDefault,Values=true"
        --query "Vpcs[0].VpcId" --output text
```

```bash
# Step 2: Create subnet lookup that references the VPC
swamp model create command/shell subnet-lookup --json
```

Edit `models/subnet-lookup/input.yaml`:

```yaml
name: subnet-lookup
version: 1
tags: {}
methods:
  execute:
    arguments:
      run: >-
        aws ec2 describe-subnets
        --filters "Name=vpc-id,Values=${{ model.vpc-lookup.resource.result.result.attributes.stdout }}"
        --query "Subnets[0].SubnetId" --output text
```

```bash
# Step 3: Create instance model that uses both lookups
swamp model create @user/ec2-instance my-instance --json
```

Edit `models/my-instance/input.yaml`:

```yaml
name: my-instance
version: 1
tags: {}
globalArguments:
  subnetId: ${{ model.subnet-lookup.resource.result.result.attributes.stdout }}
  vpcId: ${{ model.vpc-lookup.resource.result.result.attributes.stdout }}
```

```

### references/execution-drivers.md

```markdown
# Execution Drivers for Models

Execution drivers control where and how model methods run. The default driver
(`raw`) runs methods in-process. The `docker` driver runs methods in isolated
containers.

## Built-in Drivers

| Driver   | Description                                             |
| -------- | ------------------------------------------------------- |
| `raw`    | In-process execution in the host Deno process (default) |
| `docker` | Isolated execution in Docker containers                 |

## CLI Override

Override the driver for a single method run:

```bash
swamp model method run my-model execute --driver docker --json
```

The CLI flag takes highest priority over all other driver settings.

## Definition YAML

Set the driver on a model definition so it always runs in a container:

```yaml
id: 550e8400-e29b-41d4-a716-446655440000
name: my-model
version: 1
driver: docker
driverConfig:
  image: "alpine:latest"
  timeout: 30000
methods:
  execute:
    arguments:
      run: "echo hello"
```

### Docker Driver Config Fields

| Field         | Required | Description                                  |
| ------------- | -------- | -------------------------------------------- |
| `image`       | Yes      | Docker image to run                          |
| `bundleImage` | No       | Image for bundle mode (must have Deno)       |
| `command`     | No       | CLI binary: `docker`, `podman`, or `nerdctl` |
| `timeout`     | No       | Timeout in milliseconds                      |
| `network`     | No       | Docker network to attach                     |
| `memory`      | No       | Memory limit (e.g. `512m`)                   |
| `cpus`        | No       | CPU limit (e.g. `1.5`)                       |
| `volumes`     | No       | Volume mounts (e.g. `["/host:/container"]`)  |
| `env`         | No       | Environment variables                        |
| `extraArgs`   | No       | Additional docker run flags                  |

### Using Podman or nerdctl

Set `command` to use an alternative container runtime:

```yaml
driverConfig:
  command: "podman"
  image: "alpine:latest"
```

## Resolution Priority

When a model is run via a workflow, the driver is resolved from multiple sources
(first match wins):

1. CLI `--driver` flag
2. Workflow step `driver:` field
3. Workflow job `driver:` field
4. Workflow-level `driver:` field
5. Model definition `driver:` field
6. Default: `raw`

## Extension Model Compatibility

Extension models (TypeScript models in `extensions/models/`) work with the
Docker driver with no code changes. Swamp automatically generates a
self-contained bundle that inlines all dependencies (including zod) for
container execution. See the `swamp-extension-model` skill for details.

## Design Reference

For architecture details, execution flow diagrams, and implementation files, see
[design/execution-drivers.md](design/execution-drivers.md).

```

swamp-model | SkillHub