Fix template example runs: use per-step folders instead of flat files

The workflow spec requires one folder per step inside runs/example/
(e.g. 1-intake/results.md), not flat files. Fixed browser-recipes,
seo-pipeline, and support-ops. release-ops was already correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
bernatsampera
2026-08-09 18:16:25 +02:00
co-authored by Claude Opus 4.6
parent 5fa3f6bffd
commit 87d8575829
64 changed files with 1751 additions and 0 deletions
@@ -0,0 +1,19 @@
---
description: Run an existing recipe by name. Falls back to full exploration if the script fails.
parameters:
- name: recipe
description: The recipe name to execute (e.g. "export-monthly-report")
required: true
---
# Run Recipe
Run a saved recipe from the library. If the recipe fails, fall back to the full exploration workflow and update the recipe.
1. Read `recipes/index.md` to find the recipe named `$recipe`.
2. If the recipe is not found, report the error and list available recipes. Stop.
3. Read `recipes/$recipe.md` to get the recipe metadata: parameters, description, and script path.
4. Ask the user for any required parameter values not already provided.
5. Run the script `recipes/$recipe.py` with the provided parameters.
6. If the script succeeds: report the result. Done.
7. If the script fails: report the error. Fall back to the full workflow (steps 0 through 3) using the original action description from the recipe metadata. The re-exploration will produce an updated recipe that replaces the broken one.
@@ -0,0 +1,23 @@
---
description: Connect your browser automation interface and verify access.
---
# Setup
Read this workflow's `index.md` and `steps/index.md` first to understand what browser-recipes does and what it needs.
## 1. Identify the browser automation
Ask the user which browser automation they have available: Chrome CDP, Playwright, Puppeteer, or another. Find the matching connection in the agent's environment, or help the user create one.
## 2. Verify access
Smoke test: open a simple URL (e.g. https://example.com) in the browser to confirm the connection works. Present what the browser sees (page title, visible text) as proof.
## 3. Report
If the smoke test passes, report setup complete. If it fails, diagnose and fix the connection before declaring done.
## What setup creates
Nothing. The `recipes/` folder and `runs/example/` ship with the template. Real recipes and real runs are created during use.
+34
View File
@@ -0,0 +1,34 @@
---
id: browser-recipes
name: Browser Recipes
description: >
Explore browser actions with AI, then crystallize them into reusable Python
scripts. Recipes self-heal: when a script fails, the agent falls back to
live exploration and updates the recipe.
parameters:
- name: action
description: What to do, in plain words (e.g. "export the monthly report from the admin dashboard")
required: true
connections:
- kind: browser
description: A browser automation interface (Chrome CDP, Playwright, or similar)
tags: [automation, browser]
---
Turn a plain-language browser action into a reusable Python script. The agent explores the target site with AI-driven browser control, records every step, and produces a parameterized recipe. On future runs, the recipe executes directly (fast, no AI needed). If a recipe fails, the agent re-explores and updates it automatically.
The `action` parameter is a plain-language description of what to do. Examples: "export the monthly report from the admin dashboard", "change the team name in the settings page", "download all invoices for Q2".
## Run naming
Each run folder is named with a kebab-case slug derived from the action description. For example, "export the monthly sales report" becomes `export-monthly-sales-report`. The slug is decided during step 0 (analyze).
## How it learns
The workflow accumulates knowledge in one place:
1. **Recipes** (`recipes/`): parameterized Python scripts for browser actions the agent has performed before. Step 0 (analyze) checks this library first. If a recipe matches, the agent runs it directly via the `run-recipe` command. Step 3 (test) saves new recipes here after a successful test. Each recipe has a `.py` file (the script) and a `.md` file (metadata, parameters, origin).
2. **Run history** (`runs/`): every completed action is a structured folder with analysis, exploration log, recipe proposal, and outcome. The agent can search past runs to find how a similar action was handled before.
When a saved recipe fails, the agent falls back to the full four-step workflow. The re-exploration produces an updated recipe that replaces the broken one. Recipes improve over time as sites change.
@@ -0,0 +1,9 @@
# Recipes
Reusable Python scripts for browser actions the agent has performed before. Each recipe has two files: a `.py` file (the executable script) and a `.md` file (metadata, parameters, origin).
The agent consults this index during step 0 (analyze) to find existing recipes. Step 3 (test) adds new entries here after a successful test. When a recipe fails during `run-recipe`, the agent re-explores and updates the recipe automatically.
## Index
_(no recipes yet, they are created as the workflow runs)_
@@ -0,0 +1,10 @@
# Analysis: export-monthly-report
- **Target**: https://app.acme.com/dashboard
- **Goal**: Download the monthly sales report as a CSV file.
- **Success definition**: A file named `sales-{month}.csv` is downloaded to the local filesystem.
- **Complexity**: Multi-step (login, navigate to reports, select month, click export).
## Notes
The dashboard requires authentication. The user must be logged in before the export action can start. The report page loads data asynchronously, so the export button only appears after the data finishes loading.
@@ -0,0 +1,33 @@
# Exploration: export-monthly-report
## Action sequence
1. **Open the dashboard**: Navigate to `https://app.acme.com/dashboard`.
- Selector: n/a (direct URL)
- Expected state: Dashboard home page loads. The sidebar is visible with navigation links.
2. **Click "Reports" in the sidebar**: Navigate to the reports section.
- Selector: `nav a[href="/reports"]`
- Expected state: The reports page loads with a list of report categories.
3. **Click "Sales"**: Open the sales reports view.
- Selector: `a[data-report="sales"]`
- Expected state: The sales report page loads. A month dropdown and an export button are visible. The export button is disabled until data loads.
4. **Select the target month**: Choose "June 2026" from the month dropdown.
- Selector: `select#report-month`
- Value: `2026-06`
- Expected state: The report data reloads for the selected month. A loading spinner appears, then the `.report-ready` indicator becomes visible.
5. **Wait for the report to finish loading**: The export button enables only after loading completes.
- Selector: `.report-ready` (wait for this element to appear)
- Expected state: The export button is now enabled (no longer has the `disabled` attribute).
6. **Click "Export CSV"**: Download the report file.
- Selector: `button.export-csv`
- Expected state: A file download starts. The file name is `sales-2026-06.csv`.
## Branching
- If a "Session expired" modal appears at any point, close it and re-authenticate before continuing.
- If the report data fails to load (error banner appears), refresh the page and retry from step 3.
@@ -0,0 +1,64 @@
# Recipe proposal: export-monthly-report
## Recipe name
`export-monthly-report`
## Parameters
| Name | Type | Description |
|--------|--------|--------------------------------------------------|
| month | string | The target month in YYYY-MM format (e.g. "2026-06") |
| format | string | Output format: "csv" or "xlsx" (default: "csv") |
## Script
```python
"""Export the monthly sales report from app.acme.com."""
import argparse
def run(browser, month: str, format: str = "csv"):
"""Navigate the admin dashboard and download the sales report."""
# 1. Open the dashboard
browser.goto("https://app.acme.com/dashboard")
# 2. Click Reports in the sidebar
browser.click('nav a[href="/reports"]')
browser.wait_for_selector('a[data-report="sales"]')
# 3. Click Sales
browser.click('a[data-report="sales"]')
browser.wait_for_selector("select#report-month")
# 4. Select the target month
browser.select("select#report-month", month)
# 5. Wait for the report to finish loading
browser.wait_for_selector(".report-ready", timeout=30000)
# 6. Click the export button
export_selector = f"button.export-{format}"
browser.click(export_selector)
# 7. Wait for the download to complete
downloaded = browser.wait_for_download(timeout=15000)
return downloaded
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--month", required=True)
parser.add_argument("--format", default="csv", choices=["csv", "xlsx"])
args = parser.parse_args()
# browser is injected by the runtime
run(browser, args.month, args.format)
```
## Test plan
1. Run with `month=2026-06`, `format=csv`.
2. Verify that a file named `sales-2026-06.csv` is downloaded.
3. Check that the file is not empty and contains CSV headers.
@@ -0,0 +1,9 @@
# Done: export-monthly-report
## What was achieved
Recipe "export-monthly-report" created and saved to recipes/. The script navigates app.acme.com, selects the target month, and downloads the sales report. Tested with month=2026-06, format=csv.
## What was learned
The export button only appears after the report finishes loading. The script waits for the `.report-ready` indicator before clicking export. This wait is baked into the recipe.
@@ -0,0 +1,13 @@
# Run: export-monthly-report
- **Action**: Export the monthly sales report from the admin dashboard
- **Target**: app.acme.com/dashboard
- **Date**: 2026-07-20
- **Status**: done
| Step | Status |
|------|--------|
| 0-analyze | done |
| 1-explore | done |
| 2-propose | done |
| 3-test | done |
@@ -0,0 +1,31 @@
# Step 0: Analyze the action
## Purpose
Understand what the user wants to do. Identify the target site or app, the goal, and define what success looks like after the action completes.
## Input
- `action` parameter: a plain-language description of the browser action.
- `recipes/index.md`: the recipe library, to check for existing matches.
## Output
`runs/{slug}/0-analysis.md` with:
- **Target**: the URL or app to operate on.
- **Goal**: what to achieve, in one sentence.
- **Success definition**: how to verify the action worked (e.g. a file downloaded, a value changed, a confirmation message appeared).
- **Complexity**: single page, multi-step, or requires auth.
## How to execute
1. Parse the action description. Identify the target site, the operation, and the expected result.
2. If the target is unclear, ask the user for the URL or app name.
3. Derive a kebab-case slug from the action description (e.g. "export the monthly report" becomes `export-monthly-report`).
4. Check `recipes/index.md` for an existing recipe that matches this action or a similar one. If a recipe exists, tell the user and suggest the `run-recipe` command instead. Stop here if the user agrees.
5. Write the analysis file. Present it to the user for confirmation.
## Done when
The analysis file is written and the user has confirmed it.
@@ -0,0 +1,31 @@
# Step 1: Explore with the browser
## Purpose
Navigate the target site or app with the browser connection. Find the path from start to goal. Record every step: what to click, what to wait for, what to fill in.
## Input
- The analysis from step 0: target, goal, success definition.
- The browser connection.
## Output
`runs/{slug}/1-exploration.md` with:
- **Action sequence**: a numbered list of every action performed.
- **Selectors**: the CSS selector or element identifier for each action.
- **Expected state**: what the page should look like after each action.
- **Branching**: any conditional paths (e.g. "if a modal appears, close it first").
## How to execute
1. Open the target URL in the browser.
2. Navigate step by step toward the goal. At each step, record the action, the selector, and the result.
3. If you hit a blocker (login required, captcha, unexpected state), document it and ask the user for help.
4. When the goal is achieved, verify the result against the success definition from step 0.
5. Write the exploration log.
## Done when
The goal is achieved and the full action sequence is recorded in the exploration file.
@@ -0,0 +1,30 @@
# Step 2: Propose the recipe
## Purpose
Turn the exploration into a Python script with parameters. Define what is variable (changes per run) and what is fixed (same every time).
## Input
- The exploration log from step 1: action sequence, selectors, expected states.
## Output
`runs/{slug}/2-recipe.md` with:
- **Recipe name**: kebab-case identifier (e.g. `export-monthly-report`).
- **Parameters**: for each, the name, type, and description.
- **Script**: the full Python source that uses the browser connection to execute the action.
- **Test plan**: how to verify the script works (input values, expected result).
## How to execute
1. Review the exploration log. Identify which values should be parameters (URLs, dates, form values, file paths) and which are fixed navigation steps.
2. Write a Python script that uses the browser connection to execute the action. The script accepts parameters as arguments.
3. Include waits and assertions from the exploration (e.g. wait for an element before clicking, verify a confirmation message).
4. Present the recipe to the user for review.
5. Make adjustments based on feedback.
## Done when
The user has approved the recipe: the script, parameters, and test plan.
+41
View File
@@ -0,0 +1,41 @@
# Step 3: Test and save the recipe
## Purpose
Run the proposed script. If it works, save it to the recipe library. If it fails, go back to step 1 and re-explore.
## Input
- The recipe from step 2: script, parameters, test plan.
- The browser connection.
## Output
On success:
- `recipes/{name}.py`: the Python script.
- `recipes/{name}.md`: recipe metadata (name, description, parameters, creation date, source run).
- `recipes/index.md`: updated with the new entry.
- `runs/{slug}/done/info.md`: the run completion record.
On failure:
- The failure documented in the run folder. Go back to step 1 with the failure context.
## How to execute
1. Run the Python script with the test parameters from the test plan.
2. Verify the result against the success definition from step 0.
3. If the script succeeds:
1. Save the script to `recipes/{name}.py`.
2. Write `recipes/{name}.md` with the recipe metadata: name, description, parameters (name, type, description for each), creation date, and the source run slug.
3. Update `recipes/index.md` with a new entry for this recipe.
4. Write `runs/{slug}/done/info.md` with what was achieved and what was learned.
4. If the script fails:
1. Document the failure: what went wrong, which step broke, the error message.
2. Go back to step 1 with the failure context so the re-exploration can address the problem.
3. The updated exploration will produce a new recipe proposal in step 2.
## Done when
The recipe is saved to `recipes/` and the run is closed, OR the failure is documented and step 1 is re-entered.
+6
View File
@@ -0,0 +1,6 @@
# Steps
0. [Analyze](0-analyze.md): understand the action, check for existing recipes, define success.
1. [Explore](1-explore.md): navigate the target site with the browser, record every step.
2. [Propose](2-propose.md): turn the exploration into a parameterized Python recipe.
3. [Test](3-test.md): run the recipe, save it on success, re-explore on failure.
+93
View File
@@ -0,0 +1,93 @@
---
description: Set up release-ops for your project. Maps your source control, registry, and notification connections, detects your stack, and verifies access.
---
# Setup
Read this workflow's `index.md` and `steps/index.md` first to understand what release-ops does and what it needs.
## 1. Detect the project stack
Scan the repository root for version files and build tools:
- `pyproject.toml` with `[project]` or `[tool.poetry]`: Python project. Build with `uv build`, publish with `uv publish`.
- `package.json`: Node project. Build with `npm pack`, publish with `npm publish`.
- `Cargo.toml`: Rust project. Build and publish with `cargo publish`.
- `setup.cfg` or `setup.py`: legacy Python. Note it and ask the user for their preferred build tool.
- Other: ask the user what build and publish commands to use.
Record the detected stack, version file paths, build command, and publish command. Present the detection to the user for confirmation.
## 2. Bind source control
Ask the user which git host they use (GitHub, GitLab, Bitbucket, or other). Find the matching connection in the agent's environment, or help the user create one.
Verify access: query the remote for the repository's recent tags. Present one tag as proof.
## 3. Map the package registry (optional)
Ask the user if this project is published to a package registry. If yes, identify the registry and find the matching connection.
Verify access: run a dry auth check against the registry (e.g. `uv publish --check`, `npm whoami`). If the user says there is no registry, record "no registry" and the publish step will be skipped during runs.
## 4. Map the notification sink (optional)
Ask the user if they want release announcements posted somewhere (Slack, Discord, email, or other). If yes, find the matching connection and ask which channel or recipient to use.
If the user does not want announcements, record "no notification sink" and the announce step will be skipped during runs.
## 5. Configure the test command (optional)
Ask the user if they want the preflight step to run a test suite before each release. If yes, record the test command (e.g. `uv run pytest`, `npm test`, `cargo test`).
If not, preflight will only check git and registry state.
## 6. Create the release history
Create `releases/log.md` if it does not exist:
```markdown
# Release History
| Version | Date | Summary | Notes |
|---------|------|---------|-------|
```
Create `releases/insights.md` if it does not exist:
```markdown
# Release Insights
This file is updated automatically after each release run.
## Commit style
Not yet detected. Will be set after the first run.
## Known issues
None recorded yet.
## Changelog style
Not yet detected. Will be learned from existing changelog or first approved draft.
```
## 7. Smoke test
Run a dry preflight:
1. Check that the working tree is clean.
2. Find the most recent version tag.
3. List the commits since that tag (limit to 5).
4. Present the summary.
Do not make any changes. This is read-only.
If the smoke test passes, report setup complete. If it fails, diagnose and fix before declaring done.
## What setup creates
- `releases/log.md` (empty release history table)
- `releases/insights.md` (empty insights, populated after first run)
- Nothing else. The example run and step files ship with the template.
+39
View File
@@ -0,0 +1,39 @@
---
id: release-ops
name: Release Ops
description: >
A release workflow that collects changes, drafts a changelog, bumps the
version, publishes, and optionally announces. Each release is a searchable
run; the workflow learns your changelog style and known blockers over time.
parameters:
- name: version
description: Target version (e.g. "0.5.0") or omit for agent proposal based on changes
required: false
- name: scope
description: Limit to a specific package in a monorepo (e.g. "@acme/core")
required: false
connections:
- kind: source-control
description: The git host where the repository lives (GitHub, GitLab, or similar)
- kind: package-registry
description: The registry to publish to (PyPI, npm, crates.io, or similar). Optional; skip if the project has no published package.
- kind: notification-sink
description: Where to post release announcements (Slack, Discord, email, or similar). Optional; skip if not needed.
tags: [release, ops, devtools]
---
Ship a release with a repeatable six-step procedure. Each run takes one release from preflight to announcement, logs the result, and feeds learnings back into future runs.
The `version` parameter accepts an explicit semver string (e.g. "1.2.0") or can be omitted. When omitted, the agent proposes major/minor/patch based on the changes collected in step 1. The agent always asks for confirmation before applying a version bump.
The `scope` parameter is for monorepos. When set, the workflow limits change collection, version bumping, and publishing to the named package. When omitted, the workflow operates on the entire repository.
Run folders are named by version: `v{version}` (e.g. `v0.5.0`). If a second release happens the same day with the same version prefix, append `-b` (e.g. `v0.5.0-b`).
## How it learns
The workflow accumulates knowledge in two ways:
1. **Release history** (`releases/log.md`): a table of every release with version, date, summary, and notes. The agent consults it to understand release cadence and past decisions.
2. **Insights** (`releases/insights.md`): created after the first run. Records the repo's changelog style (tone, grouping, detail level), known-flaky checks, common blockers, and workarounds. The agent consults this during preflight and changelog drafting.
@@ -0,0 +1,4 @@
# Parameters
- **version**: (not provided, agent proposed 0.3.0)
- **scope**: (not set, full repo)
@@ -0,0 +1,25 @@
# Collected Changes
**Since tag**: v0.2.1
**Style**: freeform (3/8 commits use conventional prefixes)
**Contributors**: Ana Torres, Diego Vega
## Commits
| Hash | Author | Message | Classification |
|------|--------|---------|---------------|
| a1b2c3d | Ana Torres | Add streaming export for large datasets | feature |
| e4f5g6h | Diego Vega | Fix memory leak in batch processor | fix |
| i7j8k9l | Ana Torres | Support Parquet output format | feature |
| m0n1o2p | Diego Vega | Fix CSV quoting for fields with commas | fix |
| q3r4s5t | Ana Torres | Update README with streaming examples | docs |
| u6v7w8x | Diego Vega | Refactor internal buffer pool | refactor |
| y9z0a1b | Ana Torres | Add progress callback to export API | feature |
| c2d3e4f | Diego Vega | Bump dev dependencies | chore |
## Summary
- 3 features (streaming export, Parquet support, progress callback)
- 2 fixes (memory leak, CSV quoting)
- 1 docs, 1 refactor, 1 chore
- No breaking changes
@@ -0,0 +1,14 @@
# Changelog Draft
## [0.3.0] - 2026-07-20
### Added
- Streaming export for large datasets: process millions of rows without loading them all into memory.
- Parquet output format: export directly to `.parquet` files alongside CSV and JSON.
- Progress callback on the export API: track export progress in real time.
### Fixed
- Memory leak in the batch processor when processing more than 100k rows.
- CSV quoting for fields that contain commas or newlines.
@@ -0,0 +1,20 @@
# Version Bump
- **Old version**: 0.2.1
- **New version**: 0.3.0
- **Reasoning**: 3 new features, no breaking changes. Minor bump.
- **User confirmed**: yes
## Files modified
| File | Change |
|------|--------|
| pyproject.toml | version = "0.2.1" -> version = "0.3.0" |
| src/datakit/__init__.py | __version__ = "0.2.1" -> __version__ = "0.3.0" |
| CHANGELOG.md | Inserted 0.3.0 entry at the top |
## Commit
```
release: v0.3.0
```
@@ -0,0 +1,14 @@
# Publish
- **Tag**: v0.3.0 (annotated, pushed)
- **GitHub Release**: created with changelog body
- **Registry**: PyPI
- **Build**: `uv build` (success, built sdist + wheel)
- **Publish**: `uv publish` (success)
- **Verification**: datakit 0.3.0 visible on PyPI
## Release log entry
| Version | Date | Summary | Notes |
|---------|------|---------|-------|
| 0.3.0 | 2026-07-20 | Streaming export, Parquet support, progress callback, 2 bug fixes | First release with Parquet |
@@ -0,0 +1,15 @@
# Done: v0.3.0
## What was released
datakit 0.3.0 published to PyPI. Three new features (streaming export, Parquet output, progress callback) and two bug fixes (memory leak in batch processor, CSV quoting).
## Announcement
Posted to #releases on Slack with a link to the GitHub Release page.
## What was learned
- The repo uses freeform commit messages (not conventional commits). The agent used diff-based classification. Recorded in insights.
- The existing CHANGELOG.md uses Keep-a-Changelog style with Added/Fixed/Changed sections. Recorded for future drafts.
- No flaky tests or blockers encountered.
@@ -0,0 +1,18 @@
# Run: v0.3.0
- **Version**: 0.3.0
- **Date**: 2026-07-20
- **Package**: datakit
- **Registry**: PyPI
- **Outcome**: Published datakit 0.3.0 to PyPI. Announced in #releases on Slack.
## Steps
| Step | Status |
|------|--------|
| 0 - Preflight | done |
| 1 - Collect | done |
| 2 - Changelog | done |
| 3 - Bump | done |
| 4 - Publish | done |
| 5 - Announce | done |
@@ -0,0 +1,31 @@
# 0 - Preflight
## Purpose
Verify that the repository is in a releasable state before any work starts. Catch problems early so the release does not fail midway.
## Input
- The repository's current git state.
- The `scope` parameter (if set, check only the scoped package).
- `releases/insights.md` (if it exists): known-flaky checks and past blockers.
- The test command recorded during setup (if any).
## Output
Write `0-preflight.md` in the run folder with a checklist of every check and its result (pass/fail/skip).
## How to execute
Run these checks in order. Stop on the first failure and report it.
1. **Working tree is clean.** No uncommitted changes. If dirty, list the changed files and stop.
2. **Branch is up to date.** The current branch has no unpushed commits and is not behind the remote. Pull if behind; stop if there are conflicts.
3. **Last release tag exists.** Find the most recent tag that looks like a version (v*, semver). If no tag exists, treat the entire history as "changes since the beginning" and note this in the checklist.
4. **Registry auth works** (skip if no registry configured). Run a dry authentication check against the package registry. Method depends on the stack (e.g. `uv publish --check` for PyPI, `npm whoami` for npm). If auth fails, stop and report.
5. **Test suite passes** (skip if no test command configured). Run the test command recorded during setup. If tests fail, list the failures and stop.
6. **Known-issue scan.** If `releases/insights.md` exists, check for any known blockers or flaky tests relevant to this release. Warn the user if any match.
## Done when
All checks pass (or are skipped with a reason). The preflight checklist is written to the run folder.
+32
View File
@@ -0,0 +1,32 @@
# 1 - Collect
## Purpose
Gather all changes since the last release and classify them. This is the raw material for the changelog.
## Input
- The last release tag (from preflight).
- The `scope` parameter (if set, filter to paths owned by the scoped package).
- `releases/insights.md` (if it exists): the repo's detected commit style.
## Output
Write `1-collect/results.md` in the run folder with:
- A list of commits (hash, author, message) since the last tag.
- A classification of each commit: feature, fix, breaking change, chore, docs, refactor, or other.
- A `style` field: "conventional" if more than half the commits follow a conventional-commit format, "freeform" otherwise.
- A summary of contributors.
## How to execute
1. **Get the commit list.** Run `git log --oneline <last-tag>..HEAD`. If `scope` is set, add `-- <scope-path>` to filter.
2. **Detect commit style.** Scan commit messages for conventional-commit prefixes (feat:, fix:, chore:, docs:, refactor:, breaking:, etc.). If more than half match, mark the style as "conventional". Otherwise mark it "freeform".
3. **Classify each commit.** For conventional commits, use the prefix. For freeform commits, read the message and the diff to classify.
4. **Flag breaking changes.** Look for "BREAKING CHANGE" in commit bodies, or "!" after the type prefix (e.g. "feat!:"). Also flag commits that remove public API surface.
5. **Record in `releases/insights.md`.** If the file exists, update the detected style. If it does not exist, note the style for later creation (step 2 will create the file after the first run).
## Done when
The results file lists every commit, classified, with a style marker and contributor summary.
@@ -0,0 +1,31 @@
# 2 - Changelog
## Purpose
Draft a changelog entry from the collected changes. The draft matches the repo's existing changelog style when possible.
## Input
- `1-collect/results.md` from this run.
- The project's existing changelog file (CHANGELOG.md, CHANGES.md, or similar).
- `releases/insights.md` (if it exists): learned style preferences.
## Output
Write `2-changelog/results.md` in the run folder with the draft changelog entry.
## How to execute
1. **Find the existing changelog.** Look for CHANGELOG.md, CHANGES.md, HISTORY.md, or similar at the repo root. If none exists, note that a new one will be created.
2. **Read the style.** If a changelog exists, study the most recent 2-3 entries: heading format (## vs ###), grouping (by type, by scope, flat), bullet style, level of detail (commit-level or summary-level), tone (technical, casual, user-facing). If `releases/insights.md` has style notes, use those as a starting point.
3. **Draft the entry.** Group changes from the collect step by type (features, fixes, breaking changes, other). Use the detected style. If no style was detected, use this default:
- Heading: `## [version] - YYYY-MM-DD`
- Sections: `### Added`, `### Fixed`, `### Changed`, `### Breaking`
- One bullet per change, written for the end user (not the developer).
- Skip chore/refactor/docs unless they affect the user.
4. **Present the draft.** Show the changelog entry to the user. Ask if it looks right. Revise if requested.
5. **Update style insights.** After the user approves (or edits), note any style preferences in `releases/insights.md` for future runs.
## Done when
The user approves the changelog draft. The draft is saved in the run folder.
+37
View File
@@ -0,0 +1,37 @@
# 3 - Bump
## Purpose
Apply the version bump to the project files. Always confirm with the user before writing.
## Input
- The `version` parameter (if provided by the user).
- `1-collect/results.md`: the change classification (features, fixes, breaking changes).
- The project's version file(s): pyproject.toml, package.json, Cargo.toml, version.go, or similar.
## Output
Write `3-bump/results.md` in the run folder with: the old version, the new version, the reasoning, and the list of files modified.
## How to execute
1. **Determine the new version.**
- If the user provided an explicit `version` parameter, use it.
- If not, propose based on the changes:
- Breaking changes present: major bump.
- New features, no breaking changes: minor bump.
- Only fixes and chores: patch bump.
- Present the proposal to the user with the reasoning. Wait for confirmation.
2. **Find version files.** Scan the repo root for files that contain a version declaration: `pyproject.toml` (version = "..."), `package.json` ("version": "..."), `Cargo.toml`, `setup.cfg`, `version.py`, or any file the setup interview identified. If `scope` is set, look only in the scoped package's directory.
3. **Apply the bump.** Update the version string in every file found. Show the diff to the user before writing.
4. **Update the changelog.** Insert the approved changelog draft (from step 2) into the project's changelog file. Replace the placeholder version heading with the confirmed version number.
5. **Commit.** Stage the changed files and create a commit: `release: v{version}`. Do not push yet (step 4 handles that).
## Done when
The version is bumped in all project files, the changelog is updated, and the release commit is created (not yet pushed).
+42
View File
@@ -0,0 +1,42 @@
# 4 - Publish
## Purpose
Tag the release commit, push to the remote, and publish the package to the registry. Skip the registry publish if no registry was configured at setup.
## Input
- The version from step 3.
- The registry connection (if configured).
- The build/publish commands recorded during setup.
## Output
Write `4-publish/results.md` in the run folder with: the tag name, the push result, and the registry publish result (or "skipped").
## How to execute
1. **Create the git tag.** Tag the release commit as `v{version}`. Use an annotated tag with the changelog entry as the message.
2. **Push the commit and tag.** Push the release commit and the tag to the remote:
```
git push origin HEAD
git push origin v{version}
```
3. **Create a release on the git host** (if supported). Use the source-control connection to create a release (e.g. GitHub Release, GitLab Release) with the changelog entry as the body.
4. **Publish to the registry** (skip if not configured).
- Run the build command recorded during setup (e.g. `uv build`, `npm pack`).
- Run the publish command (e.g. `uv publish`, `npm publish`).
- Verify the published version appears on the registry.
- If publish fails, report the error. Do not roll back the tag (the user decides).
5. **Log the release.** Append a row to `releases/log.md`:
```
| {version} | {date} | {one-line summary} | {notes if any} |
```
## Done when
The tag is pushed, the registry publish succeeded (or was skipped), and the release is logged.
+35
View File
@@ -0,0 +1,35 @@
# 5 - Announce
## Purpose
Post the release notes to the configured notification sink. Skip this step entirely if no notification sink was configured at setup.
## Input
- The version and changelog entry from steps 3 and 2.
- The notification-sink connection (if configured).
- The announcement channel/recipient recorded during setup.
## Output
Write `5-announce/results.md` in the run folder with: where the announcement was posted and the message content. If skipped, write a one-line note: "No notification sink configured. Step skipped."
## How to execute
1. **Check if a notification sink is configured.** If not, write the skip note and close the step.
2. **Format the announcement.** Adapt the changelog entry to the channel's format:
- Slack/Discord: use markdown, keep it concise, link to the full release page.
- Email: use a subject line like "Released v{version}: {summary}" and the full changelog as the body.
- Other: use plain text.
3. **Post the announcement.** Send through the notification-sink connection. Confirm delivery.
4. **Update the run's done folder.** This is the last step, so create `done/info.md` with:
- What was released (version, package, registry).
- A one-line summary of the changes.
- Anything learned that should change the steps or insights.
## Done when
The announcement is posted (or skipped), and the run's `done/info.md` is written.
+8
View File
@@ -0,0 +1,8 @@
# Steps
0. [Preflight](0-preflight.md): verify the repo is ready to release. Clean tree, auth, optional tests.
1. [Collect](1-collect.md): gather changes since the last release tag. Detect commit style.
2. [Changelog](2-changelog.md): draft a changelog entry from the collected changes.
3. [Bump](3-bump.md): propose and apply the version bump. Wait for approval.
4. [Publish](4-publish.md): tag, push, and publish to the registry. Skipped if no registry.
5. [Announce](5-announce.md): post release notes to notification sink. Skipped if not configured.
+27
View File
@@ -0,0 +1,27 @@
---
description: Connect your keyword research source and verify access.
---
# Setup
Read this workflow's `index.md` and `steps/index.md` first to understand what the SEO pipeline does and what it needs.
## 1. Connect the keyword source
Ask the user which keyword research tool they use (Google Search Console, Ahrefs, Semrush, Ubersuggest, or another). Find the matching connection in the agent's environment, or help the user create one.
## 2. Understand the user's site
Ask the user about their site or niche. This context is necessary for intent matching in step 2 (evaluate). Record a short summary of the site's topic and audience in this workflow's `index.md` body or a config note.
## 3. Smoke test
Query the keyword source for a simple term related to the user's main topic. Present the top 5 results as proof that the connection works.
If the smoke test passes, report setup complete. If it fails, diagnose and fix the connection before declaring done.
## What setup creates
- A verified keyword-source connection.
- A note about the user's site context for intent matching.
- Nothing else. The `insights/` folder and `runs/example/` ship with the template. Real runs are created during use.
+42
View File
@@ -0,0 +1,42 @@
---
id: seo-pipeline
name: SEO Content Pipeline
description: >
Research keywords, discover content opportunities, and build a prioritized
list of content ideas. Each run explores a seed topic and produces actionable
suggestions, not finished articles.
parameters:
- name: seed
description: The topic or niche to explore (e.g. "BJJ gyms in Barcelona" or just "BJJ")
required: true
- name: scope
description: '"narrow" for a focused list around one niche, "broad" for cluster discovery across the seed topic'
required: true
connections:
- kind: keyword-source
description: A keyword research tool or data source (Google Search Console, Ahrefs, Semrush, or similar)
tags: [seo, content]
---
Research keywords for a seed topic, cluster them by intent, evaluate opportunities, and produce a prioritized list of content ideas. Each run delivers suggestions for what to build, not finished content.
The two parameters control every run:
- **seed**: the topic to explore. It can be broad ("BJJ") or specific ("BJJ gyms in Barcelona"). The agent uses it as the starting query against the keyword source.
- **scope**: controls how wide the research goes. "narrow" stays close to the seed and produces a focused list (3-8 clusters, 5-10 suggestions). "broad" explores adjacent topics and variations (8-20 clusters, 15-30 suggestions).
Run folders are named with a kebab-case slug derived from the seed: `{slug}` (e.g. `home-gym-equipment`, `bjj-gyms-barcelona`). The agent derives the slug during the research step.
## How it learns
The workflow accumulates cross-run findings in `insights/`:
1. **Insights** (`insights/index.md`): dated entries about keyword landscapes. Step 2 (evaluate) reads them before scoring clusters. Step 3 (suggest) updates them with new findings. Over time, insights help the agent avoid saturated niches and spot recurring opportunities.
2. **Run history** (`runs/`): every completed pipeline is a structured folder with research data, clusters, evaluation, and suggestions. The agent can search past runs to see how similar topics were handled.
## Narrow vs broad
Use "narrow" when you already know the niche and want a short, focused list of content ideas. The agent stays close to the seed and filters aggressively.
Use "broad" when you want to explore a topic space and discover clusters you had not considered. The agent expands the seed into adjacent areas and maps the full landscape before narrowing down.
+14
View File
@@ -0,0 +1,14 @@
# Insights
Cross-run findings about keyword landscapes. The agent reads this file before scoring clusters (step 2) and updates it after each run (step 3).
Format: dated entries, newest first, one line per finding.
Examples of what goes here:
- "2026-07-20: 'home gym budget' cluster is saturated, 40+ competing guides"
- "2026-07-22: 'garage gym flooring' has low competition and strong commercial intent"
## Entries
(No entries yet. Insights accumulate from real runs.)
@@ -0,0 +1,33 @@
# Research: home gym equipment
- **Seed**: home gym equipment
- **Source**: Ahrefs (keyword explorer)
- **Total keywords**: 62
## Top keywords by volume
| Keyword | Monthly volume | Difficulty | Intent |
|---------|---------------|------------|--------|
| home gym equipment | 12,100 | 67 | commercial |
| best home gym setup | 4,400 | 54 | commercial |
| garage gym ideas | 3,600 | 41 | informational |
| home gym on a budget | 2,900 | 38 | informational |
| best home gym machines | 2,400 | 61 | commercial |
| compact home gym | 1,900 | 33 | commercial |
| home gym flooring | 1,600 | 29 | informational |
| cheap home gym equipment | 1,400 | 35 | commercial |
| home gym workout plan | 1,300 | 44 | informational |
| garage gym flooring | 1,100 | 27 | informational |
| small space home gym | 980 | 31 | informational |
| home gym essentials | 950 | 48 | informational |
| home gym rack | 880 | 52 | commercial |
| dumbbell home workout | 820 | 39 | informational |
| home gym layout ideas | 740 | 25 | informational |
| best budget home gym | 680 | 36 | commercial |
| garage gym setup cost | 590 | 22 | informational |
| home gym vs gym membership | 520 | 30 | informational |
| bodyweight home workout | 480 | 42 | informational |
## Summary
The source returned 62 keywords. Commercial intent keywords dominate the high-volume end (equipment comparisons and buying guides). Informational keywords cluster around setup, flooring, and workout routines. The garage gym sub-niche has notably lower difficulty scores across the board.
@@ -0,0 +1,52 @@
# Clusters: home gym equipment
## 1. Budget home gym
Keywords about affordable setups and budget equipment lists.
- home gym on a budget (2,900/mo)
- cheap home gym equipment (1,400/mo)
- best budget home gym (680/mo)
- home gym essentials (950/mo)
- home gym vs gym membership (520/mo)
- garage gym setup cost (590/mo)
- small space home gym (980/mo)
**Intent**: informational
**Aggregate volume**: 8,020/mo
## 2. Garage gym setup
Keywords about garage conversions, flooring, and layout planning.
- garage gym ideas (3,600/mo)
- home gym flooring (1,600/mo)
- garage gym flooring (1,100/mo)
- home gym layout ideas (740/mo)
**Intent**: informational
**Aggregate volume**: 7,040/mo
## 3. Equipment reviews
Keywords about specific equipment comparisons and best-of lists.
- home gym equipment (12,100/mo)
- best home gym setup (4,400/mo)
- best home gym machines (2,400/mo)
- compact home gym (1,900/mo)
- home gym rack (880/mo)
**Intent**: commercial
**Aggregate volume**: 21,680/mo
## 4. Home workout programs
Keywords about routines and programs for home gyms.
- home gym workout plan (1,300/mo)
- dumbbell home workout (820/mo)
- bodyweight home workout (480/mo)
**Intent**: informational
**Aggregate volume**: 2,600/mo
@@ -0,0 +1,37 @@
# Evaluation: home gym equipment
## 1. Budget home gym
- **Volume**: medium
- **Difficulty**: medium
- **Intent match**: strong
- **Rating**: A
Rationale: Budget-focused searchers want practical advice and product lists. The difficulty is manageable, and the intent aligns well with affiliate or product content.
## 2. Garage gym setup
- **Volume**: medium
- **Difficulty**: low
- **Intent match**: strong
- **Rating**: A
Rationale: Garage gym keywords have the lowest difficulty scores in the set. Searchers want how-to guidance, which is straightforward to produce. The flooring sub-topic has strong commercial intent for product recommendations.
## 3. Equipment reviews
- **Volume**: high
- **Difficulty**: high
- **Intent match**: moderate
- **Rating**: B
Rationale: High volume but the top 10 results are dominated by established fitness media (Garage Gym Reviews, Wirecutter, Men's Health). A new site would struggle to rank for these head terms without significant authority.
## 4. Home workout programs
- **Volume**: low
- **Difficulty**: medium
- **Intent match**: weak
- **Rating**: C
Rationale: Workout program searchers want video content and structured plans. Dedicated fitness platforms serve this intent better. The volume does not justify the effort for a site focused on equipment or setup advice.
@@ -0,0 +1,65 @@
# Suggestions: home gym equipment
Prioritized content ideas from A-rated clusters first, then B.
## A-rated: Budget home gym
### 1. How to Build a Home Gym for Under $500
- **Target keywords**: home gym on a budget, cheap home gym equipment, best budget home gym
- **Format**: guide
- **Difficulty**: 36-38
- **Angle**: Step-by-step budget breakdown with specific product picks at three price tiers ($200, $350, $500).
### 2. Home Gym Essentials: The Only 6 Things You Actually Need
- **Target keywords**: home gym essentials, home gym on a budget
- **Format**: list post
- **Difficulty**: 38-48
- **Angle**: Cut through the noise. List the minimum viable equipment for a functional home gym, with reasoning for each pick.
### 3. Home Gym vs Gym Membership: A Cost Breakdown Over 3 Years
- **Target keywords**: home gym vs gym membership, home gym on a budget
- **Format**: comparison
- **Difficulty**: 30
- **Angle**: Concrete math comparing upfront home gym cost against monthly membership fees over 1, 2, and 3 years.
## A-rated: Garage gym setup
### 4. Garage Gym Flooring: What Actually Works (and What Doesn't)
- **Target keywords**: home gym flooring, garage gym flooring
- **Format**: guide
- **Difficulty**: 27-29
- **Angle**: Hands-on review of flooring options (stall mats, rubber tiles, foam) with cost per square foot and durability notes.
### 5. Garage Gym Layout Ideas for Single and Double Car Garages
- **Target keywords**: garage gym ideas, home gym layout ideas
- **Format**: guide
- **Difficulty**: 25-41
- **Angle**: Visual layouts with measurements for common garage sizes. Include equipment placement for different training styles.
### 6. How Much Does a Garage Gym Actually Cost?
- **Target keywords**: garage gym setup cost, garage gym ideas
- **Format**: guide
- **Difficulty**: 22
- **Angle**: Itemized cost breakdown for three tiers (basic, intermediate, full). Include flooring, equipment, and installation.
## B-rated: Equipment reviews
### 7. Compact Home Gym Setup for Small Spaces
- **Target keywords**: compact home gym, small space home gym
- **Format**: guide
- **Difficulty**: 31-33
- **Angle**: Focus on foldable and wall-mounted equipment that works in apartments or spare rooms under 100 sq ft.
### 8. Best Home Gym Machines for Beginners (2026)
- **Target keywords**: best home gym machines, best home gym setup
- **Format**: list post
- **Difficulty**: 54-61
- **Angle**: Narrow the audience to beginners to differentiate from generic best-of lists. Prioritize ease of use and value over performance.
@@ -0,0 +1,9 @@
# Done: home-gym-equipment
## What was achieved
Researched 62 keywords around "home gym equipment" (broad scope). Identified 4 clusters, rated 2 as high opportunity (budget setups and garage gym conversions). Produced 8 content suggestions, prioritized by opportunity score.
## What was learned
Equipment review keywords have high volume but the top 10 results are dominated by established fitness media. New sites should target the setup and budget angles where competition is lower and purchase intent is strong.
@@ -0,0 +1,13 @@
# Run: home-gym-equipment
- **Seed**: home gym equipment
- **Scope**: broad
- **Date**: 2026-07-18
- **Status**: done
| Step | Status |
|------|--------|
| 0-research | done |
| 1-cluster | done |
| 2-evaluate | done |
| 3-suggest | done |
@@ -0,0 +1,31 @@
# Step 0: Research keywords
## Purpose
Pull raw keyword data from the connected source for the seed topic. Collect search volumes, difficulty scores, and related terms.
## Input
- `seed` parameter: the topic to research.
- The keyword-source connection: to query for keyword data.
## Output
`runs/{slug}/0-research.md` with:
- **Seed**: the seed as entered.
- **Source**: the keyword tool used.
- **Keywords**: a table of keywords found (keyword, monthly volume, difficulty, intent type).
- **Total count**: the number of keywords collected.
## How to execute
1. Query the keyword source for the seed term.
2. Expand with related keywords, questions, and long-tail variations.
3. For "narrow" scope, stay close to the seed. For "broad" scope, explore adjacent topics and variations.
4. Record all keywords with their metrics.
5. Present a summary to the user: top 10 by volume and top 10 by opportunity (high volume, low difficulty).
## Done when
The research file is written with at least 20 keywords (narrow) or 50 keywords (broad).
+27
View File
@@ -0,0 +1,27 @@
# Step 1: Cluster keywords
## Purpose
Group the raw keywords into topic clusters by search intent and semantic similarity. Each cluster represents one potential content piece or content hub.
## Input
- The keyword list from step 0.
## Output
`runs/{slug}/1-clusters.md` with:
- **Clusters**: a list of clusters, each with a name, the keywords it contains, the dominant intent (informational, transactional, navigational, commercial), and the aggregate monthly volume.
## How to execute
1. Group keywords by topic similarity and intent.
2. Each cluster should have a clear, single topic that one piece of content could address.
3. For "narrow" scope, expect 3-8 clusters. For "broad" scope, expect 8-20 clusters.
4. Check `insights/` for any notes on these topics from previous runs.
5. Present the clusters to the user for review.
## Done when
The clusters file is written and the user confirmed the grouping makes sense.
@@ -0,0 +1,30 @@
# Step 2: Evaluate opportunities
## Purpose
Score each cluster by opportunity. Opportunity combines volume (how many people search), difficulty (how hard to rank), and intent match (how well the user's site can serve the intent).
## Input
- The clusters from step 1.
- Any relevant entries from `insights/`.
## Output
`runs/{slug}/2-evaluation.md` with:
- Each cluster scored on volume (high/medium/low), difficulty (high/medium/low), intent match (strong/moderate/weak), and an overall opportunity rating (A/B/C).
- A one-line rationale for each rating.
## How to execute
1. For each cluster, assess volume from the keyword data.
2. Assess difficulty from the keyword difficulty scores.
3. Assess intent match by comparing what the user's site offers with what searchers want. Ask the user if unclear.
4. Check `insights/` for past findings about similar topics.
5. Rate each cluster: A (high opportunity, pursue first), B (moderate, worth considering), or C (low opportunity or too competitive).
6. Present the evaluation to the user.
## Done when
All clusters are scored and the user has reviewed the evaluation.
+35
View File
@@ -0,0 +1,35 @@
# Step 3: Suggest content ideas
## Purpose
Turn the evaluated clusters into a prioritized list of concrete content ideas. Each idea has a target keyword, a working title, and a format suggestion.
## Input
- The evaluated clusters from step 2.
## Output
`runs/{slug}/3-suggestions.md` with a prioritized list. For each suggestion:
- **Target keyword(s)**: the primary and secondary keywords.
- **Working title**: a draft title for the content piece.
- **Format**: the suggested content type (guide, list post, comparison, tool page, landing page, etc.).
- **Estimated difficulty**: from the keyword data.
- **Angle**: one sentence on the approach.
Also `runs/{slug}/done/info.md` to close the run.
## How to execute
1. Focus on A-rated clusters first, then B. Skip C-rated clusters.
2. For each cluster, propose 1-3 content ideas.
3. Each idea should target specific keywords from the cluster.
4. Suggest the format that best serves the intent: informational queries get guides, commercial queries get comparisons, transactional queries get landing pages.
5. For "narrow" scope, aim for 5-10 suggestions. For "broad" scope, aim for 15-30.
6. Write the suggestions file and the `done/info.md`.
7. Update `insights/index.md` with any new findings (e.g. "topic X is saturated as of this date", "niche Y has low competition").
## Done when
The suggestions file and `done/info.md` are written. `insights/index.md` is updated if new findings emerged.
+6
View File
@@ -0,0 +1,6 @@
# Steps
0. [Research](0-research.md): pull raw keyword data from the connected source for the seed topic.
1. [Cluster](1-cluster.md): group keywords into topic clusters by intent and semantic similarity.
2. [Evaluate](2-evaluate.md): score each cluster by opportunity (volume, difficulty, intent match).
3. [Suggest](3-suggest.md): turn evaluated clusters into a prioritized list of content ideas.
+46
View File
@@ -0,0 +1,46 @@
---
description: Set up support-ops for your team. Maps your ticket tracker and product connections, creates the playbook structure, and verifies access.
---
# Setup
Read this workflow's `index.md` and `steps/index.md` first to understand what support-ops does and what it needs.
## 1. Bind the ticket tracker
Ask the user which issue tracker they use for support tickets (Linear, Jira, GitHub Issues, or another). Find the matching connection in the agent's environment, or help the user create one.
Verify access: query the tracker for recent tickets to confirm the connection works. Present one ticket title as proof.
## 2. Map the product connections
Ask the user which systems their support team operates on. These are the services where fixes happen: a database, a payment provider, an admin API, etc. There can be one or many.
For each service, find the matching connection in the agent's environment. Record each one with its read/write permission level.
## 3. Create the playbook structure
Create `playbooks/_index.md` if it does not exist. This file serves as:
- The index of all playbooks (updated as new ones are created by step 4).
- The integration mapping: how the generic playbook steps map to the user's specific connections. For each connection mapped in the previous step, write one line explaining which playbook references (e.g. "the payment provider") map to which connection.
The two example playbooks (`swap-subscription.md` and `export-member-list.md`) ship with the template. Leave them in place as format references.
## 4. Smoke test
Run a dry intake on one real ticket:
1. Query the tracker for the most recent ticket.
2. Fetch its details.
3. Identify the customer (or confirm the product connection can look them up).
4. Present the intake summary.
Do not change any ticket status or write to the product systems. This is read-only.
If the smoke test passes, report setup complete. If it fails, diagnose and fix the connection before declaring done.
## What setup creates
- `playbooks/_index.md` (the index and integration mapping)
- Nothing else. The example playbooks and the runs/example/ folder ship with the template. Real runs and real playbooks are created during use.
@@ -0,0 +1,22 @@
---
description: Resolve a support ticket end to end. Takes a ticket ID or "next" to pull from the queue.
parameters:
- name: ticket
description: The ticket ID to resolve (e.g. "TICKET-42") or "next" for the top unassigned ticket
required: true
---
# Support Task
Read this workflow's `index.md` and `steps/index.md` to load the full procedure.
Resolve the ticket `$ticket` by executing steps 0 through 5 in order:
0. **Intake**: fetch the ticket, identify the customer and category, set it to in progress.
1. **Plan**: find a matching playbook or propose a custom plan. Stop for approval.
2. **Execute**: run the plan step by step. Reads execute immediately. For every write, present what/command/impact and wait for explicit approval.
3. **Log**: write the structured run folder with intake, plan, execution log, and outcome.
4. **Learn**: create or update a playbook with what was learned.
5. **Close**: post the resolution comment and close the ticket. Both need approval.
Narrate each step as you go. Before any write to an external system, describe what you are about to do and stop for approval.
+36
View File
@@ -0,0 +1,36 @@
---
id: support-ops
name: Support Ops
description: >
A support workflow that resolves tickets, logs every action, and builds
playbooks from experience. Each resolved ticket becomes a searchable record;
repeated patterns become reusable playbooks the agent consults on future tickets.
parameters:
- name: ticket-id
description: The ticket to resolve (e.g. "TICKET-42") or "next" to pull the top item from the queue
required: true
connections:
- kind: ticket-tracker
description: The issue tracker where support tickets live (Linear, Jira, GitHub Issues, or similar)
- kind: product-api
description: The product's own API or database, for executing fixes (one or more services the team operates on)
tags: [support, ops]
---
Resolve support tickets with a repeatable six-step procedure. Each run takes one ticket from intake to close, logs every operation performed, and feeds what was learned back into playbooks for future tickets.
The `ticket-id` parameter accepts a ticket identifier from the tracker (e.g. "TICKET-42", "HELP-108") or the keyword "next" to pull the highest-priority unassigned ticket from the queue.
Run folders are named by ticket: `{ticket-id}-{slug}` where the slug is a short description of the issue (e.g. `TICKET-42-swap-membership`). The agent derives the slug from the ticket title during intake.
## How it learns
The workflow accumulates knowledge in two ways:
1. **Playbooks** (`playbooks/`): generalized procedures for recurring issue types. Step 1 (plan) consults them; step 4 (learn) creates or updates them. A new install starts with two example playbooks to show the format. Real playbooks grow from the team's own ticket history.
2. **Run history** (`runs/`): every resolved ticket is a structured folder with intake, plan, execution log, and outcome. The agent can search past runs to find how a similar issue was handled before.
## Playbook format
Each playbook in `playbooks/` follows a standard structure: When to Use, Prerequisites, Steps (each with Service, Permission level, and procedure), Common Variations (added over time from real executions), and Notes. See `playbooks/_index.md` for details and the example playbooks for the format.
+27
View File
@@ -0,0 +1,27 @@
# Playbooks
Reusable procedures for recurring support issue types. The agent consults this index during step 1 (plan) to find a matching playbook. Step 4 (learn) creates new playbooks or updates existing ones after each resolved ticket.
## Playbook format
Each playbook file follows this structure:
- **Title**: one-line description of what the playbook resolves.
- **When to Use**: the trigger conditions that indicate this playbook applies.
- **Prerequisites**: what information is needed before starting.
- **Steps**: numbered, each with Service (which connection), Permission (Read or Write), and the procedure.
- **Common Variations**: edge cases and alternative paths discovered from real executions. This section grows over time.
- **Notes**: warnings, API quirks, safety checks learned from experience.
## Integration mapping
This section maps generic playbook references to the actual connections in this agent's environment. Fill it in during setup.
- "the ticket tracker" maps to: _(fill during setup)_
- "the product database" maps to: _(fill during setup)_
- "the payment provider" maps to: _(fill during setup)_
## Index
- [swap-subscription.md](swap-subscription.md): transfer a subscription between two customer accounts.
- [export-member-list.md](export-member-list.md): export a list of active members for a given account or group.
@@ -0,0 +1,33 @@
# Export Member List
Export a list of active members for a given account, group, or organization.
## When to Use
A customer or internal team requests a list of members: names, emails, join dates, or subscription status. Common triggers: compliance requests, migration preparation, or account audits.
## Prerequisites
- The ticket must identify the scope: which account, group, or organization to export from.
- Confirm the scope exists in the product database.
## Steps
1. **Identify the scope.** Service: product database. Permission: Read. Look up the account or group. Record its ID and the expected member count if available.
2. **Query the members.** Service: product database. Permission: Read. Fetch all active members within the scope. Collect: name, email, join date, subscription status, and any other fields the ticket requests.
3. **Format the export.** No service needed. Format the data as a CSV or markdown table. Present the first few rows to the human for confirmation that the columns and data look correct.
4. **Deliver the export.** Post the export file or table as a comment on the ticket, or attach it per the team's standard delivery method.
## Common Variations
- The customer requests inactive members too; add a status filter toggle.
- The export includes payment history; join with the payment provider data.
- GDPR or privacy constraints limit which fields can be included; check with the human.
## Notes
- Member exports are read-only. No writes to any system.
- If the member count is large (hundreds or more), confirm with the human before posting the full list as a ticket comment. A file attachment may be more appropriate.
@@ -0,0 +1,37 @@
# Swap Subscription
Transfer an active subscription from one customer account to another within the same organization.
## When to Use
A customer has an active subscription linked to the wrong account. They need it moved to a different account, usually a family member, a renamed profile, or a duplicate that should not exist.
## Prerequisites
- The ticket must identify both accounts: the source (where the subscription is now) and the target (where it should go).
- Confirm both accounts exist in the product database before starting.
## Steps
1. **Fetch the source account.** Service: product database. Permission: Read. Look up the account and confirm the active subscription. Record the subscription ID, plan, and billing status.
2. **Fetch the target account.** Service: product database. Permission: Read. Confirm the account exists. Check if it already has an active subscription (if so, stop and report the conflict to the human).
3. **Update the subscription record.** Service: product database. Permission: Write. Change the account reference on the subscription from source to target. Present the exact change to the human before executing.
4. **Verify the swap.** Service: product database. Permission: Read. Re-fetch both accounts. Confirm the source no longer has the subscription and the target does.
5. **Update the payment provider.** Service: payment provider. Permission: Write. If the payment provider tracks account associations separately, update it to match. Present the change to the human before executing.
## Common Variations
_(This section grows from real executions. Examples of variations that may appear over time:)_
- The source account has multiple subscriptions; only one should move.
- The target account is in a different billing group or plan tier.
- The subscription is past due; the swap should preserve the past-due state, not reset it.
## Notes
- Never cancel and re-create a subscription as a shortcut for swapping. Cancellation triggers downstream effects (access revocation, email notifications) that are hard to reverse.
- Always verify after the swap. A successful database write does not guarantee the payment provider is in sync.
@@ -0,0 +1,3 @@
# Parameters
- **ticket-id**: HELP-307
@@ -0,0 +1,13 @@
# Intake: HELP-307
- **Ticket**: HELP-307 — "Move subscription to daughter's account"
- **Priority**: Medium
- **Labels**: billing, account
- **Reporter**: Elena Ruiz (elena.ruiz@example.com), filed 2026-07-15
- **Customer**: Elena Ruiz, account ID acc_8291, Riverside Fitness (org_412)
- **Category**: swap-subscription
- **Slug**: swap-subscription
## Summary
Elena Ruiz has an active monthly subscription (sub_4455, Premium plan, $49/mo) under her own account. She wants it moved to her daughter Maria Ruiz's account (acc_8294) because Maria is the one who uses the service. Both accounts exist in the same organization (Riverside Fitness).
@@ -0,0 +1,16 @@
# Plan: HELP-307
- **Playbook**: swap-subscription.md (exact match on category)
## Steps
1. Fetch Elena's account (acc_8291) and confirm the active subscription. Service: product database. Permission: Read.
2. Fetch Maria's account (acc_8294) and check for existing subscriptions. Service: product database. Permission: Read.
3. Update sub_4455 to point to acc_8294. Service: product database. Permission: Write.
4. Verify: re-fetch both accounts and confirm the swap. Service: product database. Permission: Read.
5. Update the payment provider customer record to reflect the new account association. Service: payment provider. Permission: Write.
## Risks
- Maria may already have a subscription. If so, stop and ask Elena which one to keep.
- The payment method is on Elena's card. Confirm with Elena whether the same card should keep paying.
@@ -0,0 +1,36 @@
# Execution Log: HELP-307
## 1. Fetch source account
- **Service**: product database
- **Operation**: Read account acc_8291
- **Details**: queried accounts collection for acc_8291
- **Result**: found Elena Ruiz, active subscription sub_4455 (Premium, $49/mo, status: active, payment method: card ending 7823)
## 2. Fetch target account
- **Service**: product database
- **Operation**: Read account acc_8294
- **Details**: queried accounts collection for acc_8294
- **Result**: found Maria Ruiz, no active subscription, same organization (org_412)
## 3. Update subscription record
- **Service**: product database
- **Operation**: Write — update sub_4455, set account_id from acc_8291 to acc_8294
- **Details**: presented change to human, approved
- **Result**: subscription sub_4455 now linked to acc_8294
## 4. Verify swap
- **Service**: product database
- **Operation**: Read accounts acc_8291 and acc_8294
- **Details**: re-fetched both accounts after the update
- **Result**: acc_8291 has no active subscription, acc_8294 has sub_4455 (Premium, active). Correct.
## 5. Update payment provider
- **Service**: payment provider
- **Operation**: Write — update customer record for sub_4455, associate with Maria Ruiz (acc_8294)
- **Details**: presented change to human, approved
- **Result**: payment provider customer record updated. Payment method (card ending 7823) remains the same per Elena's confirmation.
@@ -0,0 +1,22 @@
# Outcome: HELP-307
## Outcome
Subscription sub_4455 (Premium, $49/mo) transferred from Elena Ruiz (acc_8291) to Maria Ruiz (acc_8294). Billing continues on the same payment method (card ending 7823) as confirmed by Elena.
## Changes made
1. Product database: subscription sub_4455 account_id changed from acc_8291 to acc_8294.
2. Payment provider: customer record for sub_4455 updated to associate with acc_8294.
## Human steps
None. All operations were executed by the agent with human approval.
## Verification
Both accounts re-fetched after the swap. Elena's account shows no active subscription. Maria's account shows sub_4455 active on the Premium plan. Payment provider record matches.
## Playbook reference
Used: swap-subscription.md. Execution matched the playbook exactly. No playbook update needed.
@@ -0,0 +1,9 @@
# Done: HELP-307-swap-subscription
## What was achieved
Elena Ruiz's Premium subscription ($49/mo) was transferred to her daughter Maria Ruiz's account within the same organization (Riverside Fitness). Billing continues on the same card. Both the product database and payment provider are in sync.
## What was learned
The swap-subscription playbook covered this case exactly. No new variations or edge cases discovered. No playbook update needed.
@@ -0,0 +1,17 @@
# Run: HELP-307-swap-subscription
- **Ticket**: HELP-307
- **Category**: swap-subscription
- **Date**: 2026-07-15
- **Outcome**: Subscription transferred from Elena Ruiz to her daughter Maria Ruiz. Billing unchanged.
## Steps
| Step | Status |
|------|--------|
| 0 - Intake | done |
| 1 - Plan | done |
| 2 - Execute | done |
| 3 - Log | done |
| 4 - Learn | done (playbook matched, no update needed) |
| 5 - Close | done |
+34
View File
@@ -0,0 +1,34 @@
# Step 0: Intake
## Purpose
Accept a ticket and gather all the information needed to plan the resolution. Set the ticket to "in progress" so the team knows it is being handled.
## Input
- `ticket-id` parameter: a ticket identifier or "next".
- The ticket tracker connection: to fetch ticket details and update status.
## Output
`runs/{ticket-id}-{slug}/1-intake.md` with:
- **Ticket**: ID, title, priority, labels, link.
- **Reporter**: who filed it and when.
- **Customer**: the affected customer or account (identified from the ticket or looked up in the product).
- **Category**: the issue type (e.g. "billing-sync", "access-issue", "data-export").
- **Slug**: a short kebab-case description derived from the title.
- **Summary**: one paragraph restating the problem in plain words.
## How to execute
1. If `ticket-id` is "next", query the ticket tracker for the highest-priority unassigned ticket. Present it and wait for confirmation before proceeding.
2. Fetch the full ticket: title, description, priority, labels, all comments.
3. Identify the customer. The ticket may name them directly; if not, look them up in the product systems.
4. Classify the issue into a category slug. Check if `playbooks/` has a file that matches.
5. Set the ticket status to "in progress" in the tracker. This is the only write that does not need human approval.
6. Present the intake summary to the human.
## Done when
The intake file is written and the human has confirmed the summary is correct.
+31
View File
@@ -0,0 +1,31 @@
# Step 1: Plan
## Purpose
Decide how to resolve the ticket. Use an existing playbook if one matches; otherwise propose a custom plan.
## Input
- The intake file from step 0: category, customer, summary.
- `playbooks/`: the accumulated playbook library.
- `playbooks/_index.md`: the index and integration mapping.
## Output
`runs/{ticket-id}-{slug}/2-plan.md` with:
- **Playbook**: which playbook matched (file name), or "custom plan" if none.
- **Steps**: the numbered steps to execute, each naming which connection or service to use and whether it reads or writes.
- **Risks**: anything that could go wrong, and how to check.
## How to execute
1. Read `playbooks/_index.md` to get the playbook index.
2. Search for a playbook that matches the issue category. Check the "When to Use" section of candidate playbooks.
3. If a playbook matches: read it fully, adapt its steps to this specific ticket (fill in the customer, the specific IDs). Present the plan to the human.
4. If no playbook matches: propose a custom step-by-step plan. For each step, name which connection to use and whether the operation is a read or a write. Present the plan to the human.
5. Wait for the human to approve, modify, or reject the plan.
## Done when
The human has approved the plan and the plan file is written.
+38
View File
@@ -0,0 +1,38 @@
# Step 2: Execute
## Purpose
Execute the approved plan step by step, with human approval for every write operation.
## Input
- The plan file from step 1: the steps to execute.
- The product connections: to read and write against the product's systems.
## Output
`runs/{ticket-id}-{slug}/3-execution-log.md` with a numbered list of operations performed. Each operation records:
- **Step number**: matches the plan.
- **Service**: which connection was used.
- **Operation**: read or write, and what was done.
- **Details**: the specific identifiers, values, or queries involved.
- **Result**: what happened (success, the value returned, or the error).
## How to execute
For each step in the plan:
1. **Reads**: execute immediately. Record the result.
2. **Writes**: present a confirmation block to the human before executing:
- **What**: the operation in plain words.
- **Command**: the specific action or API call.
- **Impact**: what changes and what is affected.
Wait for explicit approval. If denied, record "skipped by human" and continue.
3. After execution, verify the result. If the result is unexpected, stop and present the situation to the human before continuing.
Record every operation in the execution log, whether it succeeded or was skipped.
## Done when
All plan steps are executed (or explicitly skipped) and the execution log is written.
+33
View File
@@ -0,0 +1,33 @@
# Step 3: Log
## Purpose
Write the complete run folder with all structured files so the resolution is permanently searchable.
## Input
- The outputs from steps 0-2: intake, plan, execution log.
## Output
The run folder `runs/{ticket-id}-{slug}/` with:
- `index.md`: run summary with ticket ID, category, one-line outcome, and status of each step.
- `1-intake.md`: from step 0 (already written).
- `2-plan.md`: from step 1 (already written).
- `3-execution-log.md`: from step 2 (already written).
- `4-outcome.md`: final state of the ticket and what changed in the product.
## How to execute
1. Write `4-outcome.md` with:
- **Outcome**: what was resolved, in one sentence.
- **Changes made**: a summary of every write operation from the execution log.
- **Human steps**: any manual actions performed outside the agent (or "None").
- **Verification**: how the fix was confirmed.
2. Write `index.md` for the run folder: ticket ID, category, date, one-line outcome, and a per-step status table (all steps should show "done" or "skipped").
3. No human approval needed for this step; it only writes to the workflow's own files.
## Done when
The run folder has all five files (index, intake, plan, execution-log, outcome) and the index shows all steps complete.
+32
View File
@@ -0,0 +1,32 @@
# Step 4: Learn
## Purpose
Feed what was learned back into the playbook library so future tickets of the same type are resolved faster.
## Input
- The plan file (step 1): which playbook was used, or "custom plan".
- The execution log (step 2): what actually happened vs. what was planned.
- `playbooks/`: the current library.
## Output
One of:
- A new playbook file in `playbooks/{category-slug}.md` if no playbook existed.
- An updated playbook with new variations or notes if one existed but the execution diverged.
- No change if the execution matched the playbook exactly.
Updated `playbooks/_index.md` if a new playbook was created.
## How to execute
1. **No playbook existed**: create one at `playbooks/{category-slug}.md` using the standard playbook format (see `playbooks/_index.md`). Generalize the steps: replace specific customer names and IDs with placeholders. Keep the procedure concrete enough that the agent can follow it on a future ticket.
2. **Playbook existed, execution diverged**: compare the plan to the execution log. Add new variations to the "Common Variations" section. Add new warnings or edge cases to "Notes". Do not remove existing content.
3. **Playbook existed, execution matched**: report "matched" and move on. No file changes.
4. Present what was created or changed to the human for review.
## Done when
The playbook library reflects what was learned from this ticket. If a new playbook was created, `playbooks/_index.md` lists it.
+26
View File
@@ -0,0 +1,26 @@
# Step 5: Close
## Purpose
Post the resolution on the ticket and close it in the tracker.
## Input
- The outcome file (step 3): what was resolved.
- The ticket tracker connection: to comment and change status.
## Output
The ticket in the tracker is commented and set to "done".
## How to execute
1. Draft a resolution comment for the ticket. Write it for the person who will read the ticket next (a support agent, the customer, or a teammate). Use plain language: name the affected customer and what changed, do not include internal IDs or technical jargon unless the audience is technical.
2. Present the draft comment to the human for approval.
3. Post the approved comment on the ticket.
4. Set the ticket status to "done" (or the equivalent closed state in the tracker).
5. Both the comment and the status change need human approval.
## Done when
The ticket has the resolution comment and is in a closed state in the tracker.
+8
View File
@@ -0,0 +1,8 @@
# Steps
0. [Intake](0-intake.md): accept a ticket, fetch its details, identify the category and slug.
1. [Plan](1-plan.md): match a playbook or propose a custom plan. Wait for approval.
2. [Execute](2-execute.md): agent+human loop. Reads run immediately; writes need approval.
3. [Log](3-log.md): write the structured run folder with all execution details.
4. [Learn](4-learn.md): create a new playbook or update an existing one with what was learned.
5. [Close](5-close.md): post the resolution on the ticket and close it.