Skip to content
Closed
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 .agents/commands/pre-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Run a subset of fast CI checks locally. These are lightweight validations that c
uv run invoke format
```

2. **Lint** (YAML, Ruff, ty, mypy, markdownlint, vale):
2. **Lint** (YAML, Ruff, ty, mypy, rumdl, vale):
```bash
uv run invoke lint
```
Expand Down
File renamed without changes.
File renamed without changes.
207 changes: 207 additions & 0 deletions .agents/skills/opsmill-dev-analyzing-bugs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
---
name: opsmill-dev-analyzing-bugs
description: >-
Performs root-cause analysis of a bug — from a GitHub issue, an issue URL, or a free-text
description — before any reproduction or fix is written. TRIGGER when: triaging or diagnosing
a bug, investigating why something misbehaves, an issue number/URL handed over for analysis,
needing the root cause before touching code. DO NOT TRIGGER when: a failing reproduction test
already exists and you are ready to fix → opsmill-dev-fixing-bugs; writing that reproduction test →
opsmill-dev-test-driving-bugs; capturing a new bug as a ticket → opsmill-dev-creating-issues.
argument-hint: <issue number or URL, or a free-text bug description>
compatibility: >-
Works in any git repo; anchors discovery on the OpsMill `dev/` layout with codebase fallback.
`gh` is optional, used only for issue numbers/URLs.
metadata:
pipeline: bug-fixing (1 of 3 — analyze → test-drive → fix)
version: 0.1.0
author: OpsMill
user-invocable: true
---

# Bug analyst

## User Input

```text
$ARGUMENTS
```

## Your role

You are a senior engineer performing root cause analysis. You do **NOT** write fixes or tests.
Your output will be consumed by `/opsmill-dev-test-driving-bugs` and `/opsmill-dev-fixing-bugs`, so be structured and precise.

## Tool usage

- Use the `Read` tool to read files -- do NOT use `cat` or `head`/`tail` in Bash.
- Use the `Glob` tool to find files -- do NOT use `find` or `ls -R` in Bash.
- Use the `Grep` tool to search file contents -- do NOT use `grep` or `rg` in Bash.
- Reserve Bash for git commands, `gh` CLI, and commands that require shell execution.
- Shell state does **not** persist across separate Bash calls (variables, `cd`); re-derive or
restate anything you need in a later snippet.

## Input

Parse `$ARGUMENTS` to determine what you are analysing:

- **Issue number or URL** (e.g. `4872` or `https://github.com/org/repo/issues/4872`): extract
the issue number. If `gh` is available, fetch the issue:

```bash
gh issue view <number>
```

- **Free-text description**: treat the text itself as the bug report.

Derive a **key** for this analysis (used to name the handoff file and downstream branch/PR):

- If an issue number is present, `<key>` = `<issue_number>-<short-slug>`.
- Otherwise `<key>` = `<short-slug>` only.

The `<short-slug>` is a lowercase, hyphenated 2--5 word summary of the bug (e.g.
`internal-groups-dropdown`). Always include the slug so concurrent analyses never collide.

This `<key>` is invented here (the slug is free-form), so it is the **canonical** one for the
whole pipeline. You will persist it -- and the downstream branch name `ai-bug-pipeline-<key>` --
into the handoff file below, so `/opsmill-dev-test-driving-bugs` and `/opsmill-dev-fixing-bugs` read them instead of re-deriving a
slug that could drift (e.g. `internal-groups-dropdown` vs `groups-dropdown-internal`).

If `$ARGUMENTS` is empty or an issue cannot be fetched, inform the developer and **STOP**.

## Discover project context (read what exists, skip what doesn't)

Anchor on the common OpsMill `dev/` structure; fall back to exploration when it is absent.
**None of these files are required.**

| File | If present, use it for |
| --- | --- |
| root `AGENTS.md` | Project map, working agreements, where code lives. |
| `dev/documentation-architecture.md` | Mapping the bug to the relevant code package(s). |
| `dev/knowledge/` | Descriptive architecture — how the affected area actually works. |
| `dev/guidelines/` | Prescriptive rules for the affected area. |

## Investigation

Follow these sections in order.

### Issue clarity check

Verify the report has enough information to work with:

| Required | Description |
|----------|-------------|
| **Clear problem statement** | Can you understand what the bug actually is? |
| **Reproduction path** | Are there steps to reproduce, OR can you infer them from the description? |
| **Expected vs actual** | Is it clear what should happen vs what happens? |

Rate the clarity:

- **CLEAR**: intent, reproduction scenario, and expected behavior are understandable (even if
some details like the affected release are missing).
- **UNCLEAR**: the intent and reproduction scenario are not understandable.

If the bug is **UNCLEAR**, inform the developer what information is missing and **STOP**.

### Investigate the codebase

1. Read root `AGENTS.md` and `dev/documentation-architecture.md` (if present) to determine which
code package(s) relate to the issue. Then:
- If you can determine the related code package, rate code identification as **RESOLVED**.
- If you cannot, rate it **EXPLORATION REQUIRED** and explore the codebase (use `Glob`/`Grep`)
until you locate the affected area.

2. Read the relevant source files in the affected area to understand the current behavior.

3. Identify the most likely root cause(s) -- point to specific files and lines.
- If you **cannot** identify a root cause after exploration, inform the developer and **STOP**.

4. Formulate a fix strategy. This is NOT the exact code -- it is the recommended approach:
- **Approach:** What should the fixer do and where? Reference existing functions/methods that
should be reused rather than reimplemented.
- **Scope:** Which files/functions need changes? How large should the change be?
- **Do NOT:** List common wrong approaches (e.g., adding a guard clause when the real fix is a
missing validation, creating new abstractions when an existing one should be reused).

## Output

Determine the repository's default branch and fetch its latest state, to record what the
analysis is based on:

```bash
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
# Fallback when origin/HEAD is unset (shallow/CI clones, manually-added remotes):
[ -z "$DEFAULT_BRANCH" ] && DEFAULT_BRANCH=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p')
[ "$DEFAULT_BRANCH" = "(unknown)" ] && DEFAULT_BRANCH="" # git prints "(unknown)" when remote HEAD is indeterminate
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
git fetch origin "$DEFAULT_BRANCH" || { echo "Cannot fetch origin/$DEFAULT_BRANCH -- set the default branch manually and retry."; exit 1; }
git rev-parse "origin/$DEFAULT_BRANCH"
```

If that fetch fails, **STOP** and report it -- do not write the analysis without a baseline
commit (the `exit 1` only fails the shell call; you must not continue to the next section).

Write the analysis to `.bug-analysis-<key>.md` in the repo root using the template below. This
file is a **local working-tree artifact, not committed** -- add `.bug-analysis-*.md` to the
repo's `.gitignore` if it is not already ignored, and never `git add` it.

Then display the full analysis to the developer in the conversation.

### Analysis template

Replace all `<placeholders>`:

````markdown
## Root cause analysis for <key>

**Key:** `<key>`
**Branch:** `ai-bug-pipeline-<key>`
**Issue:** <issue title or one-line restatement of the description>
**Based on:** `<commit SHA of origin/<default branch>>`
**Bug clarity:** CLEAR
**Code identification:** RESOLVED | EXPLORATION REQUIRED

### Root cause

<one-sentence summary>

### Affected files

- `path/to/file.ext` -- line X: <why this is the culprit>

### Explanation

<detailed reasoning>

## Fix strategy

**Approach:** <recommended fix approach -- explain WHAT to do and WHERE, not the exact code>

**Scope:** <which files/functions should need changes, and roughly how large the change should be>

**Do NOT:**

- <guardrail 1 -- common wrong approach to avoid>
- <guardrail 2 -- unnecessary refactoring to avoid>

## Notes for downstream steps

<edge cases, risks, or constraints the test-writer and fixer should know about>
````

## Quality gates

Per `../quality-gates/gates/gate-model.md`. `analyzing-bugs` is **Tier 0** (advisory output).

| Gate | Trigger | Tier | Primitives | Pass criteria | On-fail |
|---|---|---|---|---|---|
| Grounded-analysis | before writing the analysis file | T0 | P1 + self-review | Every root-cause claim cites a concrete file:line. | revise before writing |

## Common mistakes

| 🚩 Red flag | Do instead |
| --- | --- |
| Writing fix code (or a test) into the analysis | This step does analysis only — describe the fix *strategy*, not the patch; `/opsmill-dev-test-driving-bugs` and `/opsmill-dev-fixing-bugs` do the rest |
| Analysing an **UNCLEAR** bug or one with no findable root cause | STOP and tell the developer what's missing — a confident-sounding analysis of an unclear bug misleads both later steps |
| Re-deriving or guessing a slug later in the pipeline | Invent the `<key>` once here and **persist** it (and `ai-bug-pipeline-<key>`) in the handoff header so `/opsmill-dev-test-driving-bugs` / `/opsmill-dev-fixing-bugs` read it instead of drifting |
| Writing the analysis without a baseline commit | If the default-branch fetch fails, STOP — the `**Based on:**` SHA is what later steps reproduce against |
| `git add`-ing the `.bug-analysis-*.md` file | It is a local working-tree artifact — never commit it |
115 changes: 115 additions & 0 deletions .agents/skills/opsmill-dev-analyzing-dependency-bumps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
name: opsmill-dev-analyzing-dependency-bumps
description: >-
Produces a breaking-change / deprecation / opportunity report for a dependency-bump PR,
grounded in how this codebase actually uses each bumped package, not the changelog alone.
TRIGGER when: the user gives a PR URL or number for a dependency version bump (Dependabot,
Renovate, or hand-rolled) and asks what could break, what changed, or whether it is safe to
merge (e.g. "scan this dependabot PR", "what breaks if we take this bump", "is <PR url> safe
to merge"). DO NOT TRIGGER when: the PR is a feature or bugfix rather than a dependency version
bump → a code-review or bug skill instead.
argument-hint: <PR url or number for a dependency bump>
compatibility: >-
Requires GitHub access to fetch the PR (gh CLI authenticated, GitHub MCP server, or
equivalent) and a checkout of the target repo to grep real usage. Read-only — never edits,
merges, or comments unless explicitly asked.
metadata:
version: 0.1.0
author: OpsMill

user-invocable: true
---

# Analyze Dependency Bump

Given a dependency-upgrade PR, produce a breaking-change / deprecation / opportunity report **grounded in this repo's actual usage**. The governing principle:

> A changelog tells you what changed in the library. Only the codebase tells you whether it matters here.

Every claim in the report must trace to real usage in the repo — or to the verified *absence* of usage. A "safe to merge" backed only by the changelog is not allowed.

## Output contract

A **chat report** — do not write files or post PR comments unless the user explicitly asks. Lead with a one-line verdict, then one section per bumped package:

```text
Verdict: <safe to merge | needs code changes | review required>

## <package> <old> → <new>
- Classification: direct | transitive (chain: pkg ← parent ← … ← root)
- Usage in repo: <file:line refs, or "none — no imports">
- Breaking changes: <each labeled impacts-us | not-used | role-mismatch>
- Deprecations: <each + whether the repo hits it>
- Security / bug fixes: <the relevant ones only>
- Opportunities: <new APIs/defaults worth adopting, with code refs>
- Recommendation: <concrete next step>
```

Hard rules for the report:

- Every **safe** claim is backed by either no-usage (with the grep that proves it) or a verified role mismatch. Never assert safety from the changelog alone.
- Every **impacts-us** claim cites `file:line` and names the required code change.
- Keep it dry and specific. No filler, no restating the PR body.

## Workflow

Detect the ecosystem from the manifest/lockfile the PR touches and use its lockfile, reverse-dep tool, and import idiom. When a repo mixes ecosystems, run the steps once per affected ecosystem.

### 1. Fetch the PR and extract every version bump

```bash
gh pr view <num> --repo <owner/repo> --json title,body,files,headRefName,baseRefName
gh pr diff <num> --repo <owner/repo>
```

Parse the **lockfile diff** (`uv.lock`, `poetry.lock`, `package-lock.json`, `pnpm-lock.yaml`, `Cargo.lock`, `go.sum`, etc.) for exact `old → new` versions — do not trust the title alone. A bot PR names one package in the title even when the lock pulls in several. Handle both single and grouped bumps. If only a number is given and the repo is ambiguous, infer from the cwd's `git remote` or ask.

### 2. Classify each bump: direct vs transitive

This decides everything downstream.

- **Direct** — listed in the manifest (`pyproject.toml`, `package.json`, `Cargo.toml`, `go.mod`), including dev/optional/group sections.
- **Transitive** — only in the lockfile, pulled in by another package.

For transitive packages, compute the reverse-dependency chain and report it (e.g. `certifi ← httpx ← pytest-httpx`, terminating in a dev-only test tool). Depth, and whether the chain terminates in a **runtime** vs **dev/test** dependency, is a primary impact signal. Use the ecosystem's reverse-dep tool (`npm ls <pkg>`, `cargo tree -i <pkg>`, `go mod why <pkg>`, …), or parse the lockfile's resolved graph directly (e.g. `uv.lock` TOML `[[package]]` → `dependencies` / `optional-dependencies` / `dev-dependencies`) to build a reverse map — the latter needs no synced environment.

### 3. Grep real usage in the repo

Grep the repo for the package's import idiom — first to find imports of the package, then to find the specific symbols the changelog flags:

```bash
# Python example — substitute the import pattern + --include glob per ecosystem
grep -rn "import <pkg>\|from <pkg>" --include="*.py" .
grep -rn "<ChangedSymbol>\|<deprecated_fn>" --include="*.py" .
```

Guard against **symbol collisions** — a name can belong to a different library (e.g. `request.url` on an `httpx` object is not `starlette`; a `parse()` could come from any of several deps). Confirm the import source before counting a match. **Zero direct imports is a strong, citable result**: most changelog entries then cannot apply — say so plainly.

### 4. Read the full changelog for every bump (deep by default)

For every bumped package, direct or transitive, read release notes spanning the **whole** `old → new` range — breaking changes hide in intermediate versions, not just the endpoints.

- The PR body usually embeds the bot's release-notes / changelog / commits sections. Read them.
- When truncated or missing, fetch upstream with `WebFetch` (`https://github.com/<owner>/<repo>/compare/<old>...<new>`, the `CHANGELOG`), or use the Context7 MCP for the library's migration docs.
- If the notes still can't be retrieved (private upstream, no published changelog, no network), **ask the user to paste the release notes or a link** rather than guessing. Never invent changelog content — say which versions you couldn't source and report only what the repo's usage lets you verify.

Categorize each notable change: **breaking**, **deprecation**, **security fix**, **bug fix**, **new capability**.

### 5. Cross every changelog item against actual usage

Label each notable change by its relevance *to this repo*:

- **Impacts us** — the changed/removed/deprecated surface is imported and used here. Cite `file:line`, name the required change.
- **Role mismatch** — common for web frameworks and client/server libs. A client SDK is unaffected by server-side form-parsing changes (and vice versa). State the role this repo plays.
- **Not used** — the surface exists in the library but the repo never touches it. Safe.
- **Opportunity** — a new API or default that would simplify or improve existing code here; point at the adoptable code.

Be skeptical of the PR's own auto-generated migration notes (cubic / Dependabot summaries). They address the library's general audience, not this repo — verify each against the code rather than repeating it.

### 6. For large grouped bumps

Fan out: one analysis pass per package (steps 2–5), then synthesize. Breadth must not dilute grounding — a "not used" verdict still requires the grep that proves it. If the only way to cut transitive-bump noise is upstream, say so and whether pinning is worth it.

## Notes

- Pairs with PR-review skills — this one answers "what does this bump mean for us", not "is the diff well-written".
Loading