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
2 changes: 1 addition & 1 deletion .claude/commands/build-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Build and serve the `docs/` subproject locally for inspection. Stop immediately
Run each step sequentially from the repo root. If any step fails, stop and report the failure clearly.

1. **Install Ruby dependencies**: `cd docs && bundle install`
2. **Build the full site**: `make docs` from the repo root. This runs the whole pipeline in order — ts-rs bindings, the generated reference docs and hosted collection bundle (`cargo run -- docs`), the shared `webcomponents/` bundle, the copy into `docs/assets/js/`, then Jekyll.
2. **Build the full site**: `make docs` from the repo root. This runs the whole pipeline in order - ts-rs bindings, the generated reference docs and hosted collection bundle (`cargo run -- docs`), the shared `webcomponents/` bundle, the copy into `docs/assets/js/`, then Jekyll.
3. **Serve locally**: `cd docs && bundle exec jekyll serve` (run in background so the session remains interactive; serves on port 4000)
4. **Report**: Confirm the site is running at http://localhost:4000. Let the user know it auto-rebuilds on file changes.

Expand Down
50 changes: 25 additions & 25 deletions .claude/commands/new-collection.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Author a new Operator issuetype collection — a shareable AI workflow
description: Author a new Operator issuetype collection - a shareable AI workflow
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---

Expand All @@ -9,7 +9,7 @@ Create a new workflow **collection**: a named, versioned bundle of issue types t

The mechanics are easy; the design is the hard part. workflows allow agents to apply themselves in a deterministic manner.

## Vocabulary — get this right first
## Vocabulary - get this right first

Three terms that are easy to conflate:

Expand All @@ -25,7 +25,7 @@ Kanban issue types describe how a *team labels* work; a collection describes how

the term _"Workflow"_ is overloaded in the AI space. Operator workflows are json described collections of issuetypes. they can export to other workflows, such as `claude workflows` among others. As a result Operator workflows are meant to compose as broad and neutral as possible, with some opinions about structure and behavior. Define your workflows in the context of the work you want to get done.

## Step 1 — Decide what loop you are encoding
## Step 1 - Decide what loop you are encoding

Answer these before opening an editor. If you cannot answer them crisply, the
collection is not ready to write.
Expand All @@ -42,7 +42,7 @@ collection is not ready to write.
continuing, and what happens on rejection?
5. **When does it stop?** Both success and give-up conditions.

Study the shipped collections before inventing a shape — they are short and
Study the shipped collections before inventing a shape - they are short and
each encodes a real, published methodology:

```bash
Expand All @@ -52,7 +52,7 @@ cat src/collections/ralph_loop/collection.json
cat src/collections/dev_kanban/FEAT.json # the canonical multi-step example
```

## Step 2 — Create the directory
## Step 2 - Create the directory

Official/curated collections that ship in the binary live in
`src/collections/<id>/`. Community contributions live in
Expand All @@ -65,15 +65,15 @@ to every user.** When in doubt, use `collections/community/`.
<id>/
├── collection.json # the manifest
├── icon.svg # Simple Icons-shaped glyph
├── <KEY>.json # one per issue type — the Operator workflow
├── <KEY>.json # one per issue type - the Operator workflow
└── <KEY>.md # optional ticket template per issue type
```

`<id>` must match `^[a-z0-9_]{3,64}$` and equal the directory name.

## Step 3 — Write the issue types
## Step 3 - Write the issue types

One `<KEY>.json` per issue type. `KEY` matches `^[A-Z][A-Z0-9_]{1,15}$` —
One `<KEY>.json` per issue type. `KEY` matches `^[A-Z][A-Z0-9_]{1,15}$` -
**no hyphens**, because the hyphen separates the key from the ticket number in
`FEAT-123-project-summary.md`.

Expand All @@ -88,15 +88,15 @@ Required top-level fields: `key`, `name`, `description`, `mode`, `glyph`,
`fields`, `steps`. Also set `"$schema": "../../schemas/issuetype_schema.json"`
so editors validate as you type.

- **`mode`** — `autonomous` (launch and monitor; several run in parallel) or
- **`mode`** - `autonomous` (launch and monitor; several run in parallel) or
`paired` (needs you in the loop; one at a time). This is a real scheduling
constraint, not a hint. Choose `paired` only when a human genuinely must
participate throughout.
- **`glyph`** — one character shown in the TUI. Already in use across
- **`glyph`** - one character shown in the TUI. Already in use across
collections: `! # % * > ? @ B E F J L P R S T V ~`. Pick something unused and
mnemonic.
- **`color`** — one of `cyan`, `green`, `blue`, `magenta`, `yellow`, `red`.
- **`fields`** — the ticket's inputs. Types: `string`, `text`, `enum`, `bool`,
- **`color`** - one of `cyan`, `green`, `blue`, `magenta`, `yellow`, `red`.
- **`fields`** - the ticket's inputs. Types: `string`, `text`, `enum`, `bool`,
`date`, `integer`. Use `"auto": "id" | "date" | "branch" | "status"` for
values Operator fills in, and mark those `"user_editable": false`.

Expand All @@ -112,7 +112,7 @@ Steps are where the methodology actually lives. Each step is one agent session.
"prompt": "...", // Handlebars over the ticket's fields: {{ summary }}
"allowed_tools": ["Read", "Grep"], // least privilege for this step
"artifact_patterns": [".tickets/plans/{{ id }}.md"], // files that signal completion
"review_type": "plan", // none|plan|visual|pr — a gate
"review_type": "plan", // none|plan|visual|pr - a gate
"on_reject": { "goto_step": "plan", "prompt": "Plan rejected: {{ rejection_reason }}..." },
"next_step": "build" // omit on the final step
}
Expand All @@ -122,7 +122,7 @@ Rules that matter:

- **Chain with `next_step`.** Ordering follows the chain from the first step,
then appends anything unreached. Do not rely on array order alone.
- **`on_reject.goto_step` is the retry edge** — it may point backwards, and
- **`on_reject.goto_step` is the retry edge** - it may point backwards, and
usually should point at the step that can actually fix the problem (a failed
PR review goes back to `code`, not to `plan`).
- **One step, one job.** A step that plans *and* implements *and* tests gives
Expand All @@ -132,7 +132,7 @@ Rules that matter:
- **Prompts are Handlebars** over the ticket's fields. Reference only fields
you actually declared.

Beyond plain `task` steps, these types exist — use them when the shape calls
Beyond plain `task` steps, these types exist - use them when the shape calls
for it, not for novelty: `classifier`, `rag`, `delegator`, `mcp`,
`multi_model` (fan out, then vote), `multi_prompt`, `matrixed`, `pipeline`.

Expand All @@ -145,7 +145,7 @@ and Handlebars placeholders. Copy the shape from an existing one:
cat src/collections/dev_kanban/FEAT.md
```

## Step 4 — Write the manifest
## Step 4 - Write the manifest

```jsonc
{
Expand Down Expand Up @@ -178,15 +178,15 @@ cat src/collections/dev_kanban/FEAT.md
}
```

**Do not write `checksum` or `schema_checksum`** — the docs generator computes
**Do not write `checksum` or `schema_checksum`** - the docs generator computes
them at publish time. `tier: "community"` additionally requires `author`,
`url`, `license`, and `icon_path`.

`workflow_hints` is descriptive metadata (v1 does not execute it) but it is
what the catalog page displays, so it is how a reader decides whether to adopt
your collection. Write it for them, not for the parser.

## Step 5 — Draw the icon
## Step 5 - Draw the icon

A single-path 24×24 glyph. The full rules and rationale are in
`docs/design-system/` under "Brand & collection icons"; the short version:
Expand All @@ -195,10 +195,10 @@ A single-path 24×24 glyph. The full rules and rationale are in
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Display Name</title><path d="..."/></svg>
```

No `fill`, `stroke`, `width`, or `height` — the icon inherits `currentColor`
No `fill`, `stroke`, `width`, or `height` - the icon inherits `currentColor`
and its container's size. The `<title>` must equal the manifest's `name`.

Render it and *look at it* before trusting it — hand-authored path data is easy
Render it and *look at it* before trusting it - hand-authored path data is easy
to get subtly wrong, and the test checks shape, not whether the glyph reads:

```bash
Expand All @@ -210,14 +210,14 @@ magick -background white -density 384 <id>/icon.svg /tmp/icon.png
Then open `/tmp/icon.png`. If neither tool is installed, open the SVG in a
browser.

## Step 6 — Register it (embedded collections only)
## Step 6 - Register it (embedded collections only)

Skip this for `collections/community/`. For `src/collections/<id>/`, add an
entry to `EMBEDDED_COLLECTIONS` in `src/collections/mod.rs`, following the
existing entries exactly — `manifest`, `icon_svg`, and one `EmbeddedIssueType`
existing entries exactly - `manifest`, `icon_svg`, and one `EmbeddedIssueType`
per key, in the same order as the manifest.

## Step 7 — Validate
## Step 7 - Validate

Run these in order and fix anything that fails. Do not skip ahead.

Expand All @@ -242,8 +242,8 @@ make docs
cd docs/_site && python3 -m http.server 4100
```

Open `http://localhost:4100/workflows/` — your collection should appear as a
card — then its page, and step through each issue type's graph. **A workflow
Open `http://localhost:4100/workflows/` - your collection should appear as a
card - then its page, and step through each issue type's graph. **A workflow
that looks wrong as a graph is wrong.** Disconnected nodes, a reject edge
pointing somewhere useless, or a 12-step chain with no gates are all visible
at a glance and all worth fixing before shipping.
Expand Down
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
*
!operator-linux-amd64
!operator-linux-arm64
!opr8r-linux-amd64
!opr8r-linux-arm64
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
version: 2
updates:
- package-ecosystem: docker
directory: "/"
schedule:
interval: weekly

- package-ecosystem: cargo
directory: "/"
schedule:
Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,28 @@ jobs:
arguments: --all-features
command: check

- name: Set up Helm
uses: azure/setup-helm@v5.0.1

- name: Lint and render Helm chart
run: |
helm lint charts/operator
helm template operator charts/operator \
--set ingress.enabled=true \
--set ingress.host=operator.example.com \
--set ingress.tls.secretName=operator-tls \
--set networkPolicy.enabled=true \
--set bootstrap.existingSecret=operator-bootstrap \
> operator-chart.yaml

- name: Scan rendered Helm chart
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: config
scan-ref: operator-chart.yaml
severity: HIGH,CRITICAL
exit-code: '1'

# Compute the next version once, before anything is built, so the compiled
# binaries embed the same version that the release tag + Docker tag will use.
# The actual file edits + commit + tag still happen in the release job.
Expand Down Expand Up @@ -474,6 +496,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
version: ${{ needs.version.outputs.version }}
commit: ${{ steps.version_commit.outputs.commit }}
steps:
- uses: actions/checkout@v7
with:
Expand All @@ -500,6 +523,11 @@ jobs:
run: |
sed -i 's/^version: .*/version: ${{ needs.version.outputs.version }}/' docs/_config.yml

- name: Update Helm chart versions
run: |
sed -i 's/^version: .*/version: ${{ needs.version.outputs.version }}/' charts/operator/Chart.yaml
sed -i 's/^appVersion: .*/appVersion: "${{ needs.version.outputs.version }}"/' charts/operator/Chart.yaml

- name: Update package.json versions
run: |
for f in vscode-extension/package.json \
Expand Down Expand Up @@ -533,10 +561,12 @@ jobs:
"$BIN" docs --only openapi

- name: Commit version bump
id: version_commit
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add VERSION Cargo.toml Cargo.lock docs/_config.yml \
charts/operator/Chart.yaml \
vscode-extension/package.json \
vscode-extension/src/webhook-server.ts \
opr8r/Cargo.toml opr8r/Cargo.lock \
Expand All @@ -545,6 +575,7 @@ jobs:
docs/schemas/openapi.json
git commit -m "chore: bump version to v${{ needs.version.outputs.version }} [skip ci]"
git push
echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- name: Create tag
run: |
Expand Down Expand Up @@ -647,6 +678,37 @@ jobs:
untra/operator:${{ needs.release.outputs.version }}
untra/operator:latest

- name: Scan container image
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: untra/operator:${{ needs.release.outputs.version }}
scanners: vuln
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: '1'

chart:
needs: [release, docker]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.release.outputs.commit }}

- name: Set up Helm
uses: azure/setup-helm@v5.0.1

- name: Log in to GHCR
run: echo "${{ github.token }}" | helm registry login ghcr.io --username "${{ github.actor }}" --password-stdin

- name: Package and push Helm chart
run: |
helm package charts/operator --destination dist
helm push "dist/operator-${{ needs.release.outputs.version }}.tgz" oci://ghcr.io/untra/charts

deploy-docs:
needs: release
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
paths:
- 'docs/**'
- 'src/docs_gen/**'
- 'src/rest/**'
- 'src/auth/**'
- 'src/taxonomy/taxonomy.toml'
- 'src/templates/*.json'
- 'src/collections/**'
Expand Down
Loading
Loading