Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 7 additions & 126 deletions .claude/skills/swamp-data/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: swamp-data
description: Manage model data lifecycle and garbage collection. Use when listing data, viewing versions, cleaning up old data, or understanding data retention. Triggers on "data", "swamp data", "model data", "data list", "data get", "data versions", "garbage collection", "gc", "data gc", "clean up data", "old data", "data retention", "data lifecycle", "version history", "data cleanup", "prune data", "expire data", "ephemeral data".
description: List model data, view version history, delete expired versions, and run garbage collection. Use when listing data, viewing versions, cleaning up old data, or configuring data retention. Triggers on "swamp data", "model data", "data list", "data get", "data versions", "garbage collection", "gc", "data gc", "clean up data", "old data", "data retention", "data lifecycle", "version history", "data cleanup", "prune data", "expire data", "ephemeral data".
---

# Swamp Data Skill
Expand Down Expand Up @@ -340,134 +340,15 @@ swamp data gc -f --json # Skip confirmation prompt

## Accessing Data in Expressions

Use CEL expressions to access model data in workflows and model inputs.

**Note:** `model.<name>.resource.<spec>` requires the model to have previously
produced data (a method was run that called `writeResource`). If no data exists
yet, accessing `.resource` will fail with "No such key". Use
`swamp data list <model-name>` to verify data exists.

```yaml
# Access latest resource data via dot notation
value: ${{ model.my-model.resource.output.main.attributes.result }}

# Access specific version
value: ${{ data.version("my-model", "main", 2).attributes.result }}

# Access file metadata
path: ${{ model.my-model.file.content.primary.path }}
size: ${{ model.my-model.file.content.primary.size }}

# Lazy-load file contents
body: ${{ file.contents("my-model", "content") }}
```

### Data Namespace Functions

| 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 all resources across models
allResources: ${{ data.findByTag("type", "resource") }}

# Find data from a specific workflow
workflowData: ${{ data.findByTag("workflow", "my-workflow") }}

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

**Key rules:**

- `model.<name>.resource.<specName>.<instanceName>` — accesses the latest
version of a resource. Works both within a workflow run (in-memory updates)
and across workflow runs (persisted data).
- `model.<name>.file.<specName>.<instanceName>` — accesses file metadata (path,
size, contentType). Same behavior as resource expressions.
- `data.latest(modelName, dataName)` — reads persisted data snapshot taken at
workflow start.
- Use `data.version()` function for specific versions
- Use `data.findByTag()` to query across models
- See the `swamp-workflow` skill's
[data-chaining reference](../swamp-workflow/references/data-chaining.md) for
detailed guidance on expression choice in workflows.
CEL expressions access model data in workflows and model inputs. Functions,
examples, and key rules are in
[references/expressions.md](references/expressions.md).

## Data Ownership

Data artifacts are owned by the model (definition) that created them. This
ensures data integrity and prevents accidental overwrites.

### Owner Definition

Each data item tracks its owner through the `ownerDefinition` field:

| Field | Description |
| ---------------- | -------------------------------------------- |
| `ownerType` | `model-method`, `workflow-step`, or `manual` |
| `ownerRef` | Reference to the creating entity |
| `definitionHash` | Hash of the definition at creation time |
| `workflowId` | Set when created during workflow execution |
| `workflowRunId` | Specific run that created this data |

### Ownership Validation

When a model method writes data:

1. **New data**: Created with current model as owner
2. **Existing data**: Validates `ownerDefinition.definitionHash` matches
3. **Hash mismatch**: Write fails with ownership error

This prevents scenarios where multiple models accidentally share data names.

### Viewing Ownership

Use `swamp data get` to see ownership information:

```bash
swamp data get my-model state --json
```

```json
{
"name": "state",
"version": 3,
"ownerDefinition": {
"ownerType": "model-method",
"ownerRef": "my-model:create",
"definitionHash": "abc123..."
}
}
```
Data is owned by the creating model — see
[references/data-ownership.md](references/data-ownership.md) for owner fields,
validation rules, and viewing ownership.

## Data Storage

Expand Down
46 changes: 46 additions & 0 deletions .claude/skills/swamp-data/references/data-ownership.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Data Ownership

Data artifacts are owned by the model (definition) that created them. This
ensures data integrity and prevents accidental overwrites.

## Owner Definition

Each data item tracks its owner through the `ownerDefinition` field:

| Field | Description |
| ---------------- | -------------------------------------------- |
| `ownerType` | `model-method`, `workflow-step`, or `manual` |
| `ownerRef` | Reference to the creating entity |
| `definitionHash` | Hash of the definition at creation time |
| `workflowId` | Set when created during workflow execution |
| `workflowRunId` | Specific run that created this data |

## Ownership Validation

When a model method writes data:

1. **New data**: Created with current model as owner
2. **Existing data**: Validates `ownerDefinition.definitionHash` matches
3. **Hash mismatch**: Write fails with ownership error

This prevents scenarios where multiple models accidentally share data names.

## Viewing Ownership

Use `swamp data get` to see ownership information:

```bash
swamp data get my-model state --json
```

```json
{
"name": "state",
"version": 3,
"ownerDefinition": {
"ownerType": "model-method",
"ownerRef": "my-model:create",
"definitionHash": "abc123..."
}
}
```
83 changes: 83 additions & 0 deletions .claude/skills/swamp-data/references/expressions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Accessing Data in Expressions

Use CEL expressions to access model data in workflows and model inputs.

**Note:** `model.<name>.resource.<spec>` requires the model to have previously
produced data (a method was run that called `writeResource`). If no data exists
yet, accessing `.resource` will fail with "No such key". Use
`swamp data list <model-name>` to verify data exists.

```yaml
# Access latest resource data via dot notation
value: ${{ model.my-model.resource.output.main.attributes.result }}

# Access specific version
value: ${{ data.version("my-model", "main", 2).attributes.result }}

# Access file metadata
path: ${{ model.my-model.file.content.primary.path }}
size: ${{ model.my-model.file.content.primary.size }}

# Lazy-load file contents
body: ${{ file.contents("my-model", "content") }}
```

## Data Namespace Functions

| 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 all resources across models
allResources: ${{ data.findByTag("type", "resource") }}

# Find data from a specific workflow
workflowData: ${{ data.findByTag("workflow", "my-workflow") }}

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

**Key rules:**

- `model.<name>.resource.<specName>.<instanceName>` — accesses the latest
version of a resource. Works both within a workflow run (in-memory updates)
and across workflow runs (persisted data).
- `model.<name>.file.<specName>.<instanceName>` — accesses file metadata (path,
size, contentType). Same behavior as resource expressions.
- `data.latest(modelName, dataName)` — reads persisted data snapshot taken at
workflow start.
- Use `data.version()` function for specific versions
- Use `data.findByTag()` to query across models
- See the `swamp-workflow` skill's
[data-chaining reference](../../swamp-workflow/references/data-chaining.md)
for detailed guidance on expression choice in workflows.
2 changes: 1 addition & 1 deletion .claude/skills/swamp-extension-model/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: swamp-extension-model
description: Create user-defined TypeScript models for swamp. Use when users want to extend swamp with custom model types, create automation models, or add new integrations. Triggers on "create model", "new model type", "custom model", "extension model", "user model", "typescript model", "extend swamp", "build integration", "zod schema", "model plugin", "deno model", "extensions/models", "model development", "implement model".
description: Create user-defined TypeScript models for swamp — define Zod schemas, implement model interfaces, configure output specs. Use when users want to extend swamp with custom model types, create automation models, or add new integrations. Triggers on "create model", "new model type", "custom model", "extension model", "user model", "typescript model", "extend swamp", "build integration", "zod schema", "model plugin", "deno model", "extensions/models", "model development", "implement model".
---

# Swamp Extension Model
Expand Down
Loading
Loading