Skip to content
Open
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
9 changes: 6 additions & 3 deletions ai-development/claude-code-plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ Workflow logic. Anything that's "how Claude Code should approach a task" lives h
<Card title="ai-assistant-instructions" icon="book" href="/ai-development/ai-assistant-instructions">
The rules layer these plugins operate within.
</Card>
<Card title="claude-code-routines" icon="clock" href="https://github.com/JacobPEvans/claude-code-routines">
<Card title="/ship and /finalize-pr" icon="ship" href="/automation/ship-and-finalize">
The full skill lifecycle — phases, exit modes, known reliability gaps.
</Card>
<Card title="claude-code-routines" icon="clock" href="/automation/claude-code-routines">
Scheduled Claude.ai routines — the cron side of the same ecosystem.
</Card>
<Card title="ai-workflows" icon="github" href="https://github.com/JacobPEvans/ai-workflows">
The Copilot equivalent — reusable agentic workflows.
<Card title="ai-workflows" icon="github" href="/automation/ai-workflows">
Reusable GitHub Actions workflows that drive the cloud half of the pipeline.
</Card>
<Card title="Source on GitHub" icon="github" href="https://github.com/JacobPEvans/claude-code-plugins">
Plugins, hooks, full README.
Expand Down
2 changes: 1 addition & 1 deletion architecture/ai-pipeline.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Every other PAL tool has a native Claude Code equivalent; for single-model calls
</Card>
</CardGroup>

See [AI Development · Overview](/ai-development/overview) for what each one does in detail.
See [AI Development · Overview](/ai-development/overview) for what each one does in detail. For the actual triggers, callers, and skill mechanics that drive this pipeline end-to-end, see [Automation · Overview](/automation/overview).

## Observability layer

Expand Down
108 changes: 108 additions & 0 deletions automation/ai-workflows.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: "ai-workflows"
description: "Sixteen reusable GitHub Actions workflows that turn issues into draft PRs, fix CI failures automatically, and gate merges. Consumer repos wire thin callers."
tier: 2
---

> One repo of reusable workflows. Every consumer repo writes ten-line callers and inherits the entire pipeline.

[`JacobPEvans/ai-workflows`](https://github.com/JacobPEvans/ai-workflows) ships 16 reusable GitHub Actions workflows (`on: workflow_call:`) that consumer repos invoke with `uses:` references. The AI orchestration, prompts, and rate guards live in the upstream workflow; the consumer caller declares the trigger and inherits secrets.

## Event-triggered workflows

These run on GitHub events. Wire one caller per workflow you want.

| Workflow | Trigger | What it does |
| --- | --- | --- |
| `issue-triage.yml` | `issues: [opened]` | Categorizes, deduplicates, labels new issues |
| `issue-resolver.yml` | `issues: [opened]` | Creates a draft PR when the issue is well-scoped and not excluded by labels |
| `ci-fix.yml` | `workflow_run` of your CI workflow, `conclusion: failure` | Reads the failed log, pushes a fix commit |
| `final-pr-review.yml` | `pull_request_review: [submitted]` | Final merge-readiness gate before human merge |
| `project-router.yml` | `issues/pull_request: [opened, labeled]` | Routes items to GitHub Projects with smart field assignment |
| `post-merge-docs-review.yml` | `push: [main]` → dispatch | After merge, audits docs touched by the commit, creates fix PRs |
| `post-merge-tests.yml` | `push: [main]` → dispatch | After merge, analyzes the code changes and drafts targeted tests |
| `issue-linker.yml` | `pull_request: [opened, closed]` | Links open issues to PRs on open; closes resolved issues on merge |
| `notify-ai-pr.yml` | `pull_request: [opened]` from a bot | Slack notification when an AI-authored PR opens |

## Scheduled workflows

These run on cron — typically called with `schedule:` and a manual `workflow_dispatch:`.

| Workflow | Default schedule | What it does |
| --- | --- | --- |
| `code-simplifier.yml` | Daily 04:00 UTC | DRY enforcement, dead code removal, drafts PRs |
| `best-practices.yml` | Weekly Wed 03:00 UTC | Audit creating actionable recommendations |
| `next-steps.yml` | Daily 05:00 UTC | Analyzes merge momentum, suggests next logical action |
| `issue-sweeper.yml` | Weekly Mon 06:00 UTC | Scans open issues, comments on progress, closes resolved |
| `issue-hygiene.yml` | Weekly Mon 07:00 UTC | Detects duplicates, links merged PRs, flags stale issues |
| `label-sync.yml` | Weekly Sun 05:00 UTC | Syncs canonical labels from the `.github` repo to consumers |
| `repo-orchestrator.yml` | `workflow_dispatch` | Multi-repo hub-and-spoke dispatcher for ad-hoc operations |

## What's deprecated or disabled

| Workflow | Status | Replacement |
| --- | --- | --- |
| `claude-review.yml` | DEPRECATED 2026-04-04. All jobs gated `if: false`. | External Gemini + Copilot PR reviews |
| `pr-issue-linker.yml` | Auto-triggers explicitly disabled (`workflow_dispatch` only) | `issue-linker.yml` |

Wiring a caller for a deprecated workflow is a no-op — the consumer caller runs, the upstream silently skips. Don't.

## How a caller looks

A consumer caller is the smallest YAML that declares a trigger, sets permissions, and forwards to the upstream:

```yaml
name: Issue Triage
on:
issues:
types: [opened]
permissions:
contents: read
id-token: write
issues: write
jobs:
run:
uses: JacobPEvans/ai-workflows/.github/workflows/issue-triage.yml@main
secrets: inherit
```

Permission shape varies per workflow — `issue-resolver` needs `pull-requests: write`, `ci-fix` needs `actions: read`, `post-merge-*` needs `actions: write` for the re-dispatch. The [canonical caller templates](https://github.com/JacobPEvans/ai-workflows/blob/main/docs/GETTING_STARTED.md) list the exact permission block for each.

## Versioning

Per [`ci-cd-policy.md`](https://github.com/JacobPEvans/ai-assistant-instructions/blob/main/agentsmd/rules/ci-cd-policy.md), JacobPEvans self-references use `@main` or a major tag like `@v0` — never minor/patch pins. The full SemVer tags exist (`@v0.15.1`) and are tracked by Renovate, but consumers should ride a moving ref so upstream improvements land without a Renovate PR per repo.

## Authentication

Every reusable workflow uses [`anthropics/claude-code-action@v1`](https://github.com/anthropics/claude-code-action), authenticated directly against the Anthropic API:

- `secrets.ANTHROPIC_API_KEY` — Anthropic API key. Spend draws from the Claude Max plan's $200/month automation credits.

OAuth tokens from a Claude Code subscription session are **explicitly forbidden** in unattended CI — they violate the [Claude Code Terms of Service](https://www.anthropic.com/legal/terms). OpenRouter is supported as a fallback provider (set `secrets.OPENROUTER_API_KEY` and `secrets.OPENROUTER_BASE_URL`) but is no longer the default. The provider matrix lives in [AUTHENTICATION.md](https://github.com/JacobPEvans/ai-workflows/blob/main/docs/AUTHENTICATION.md).

## Commit signing

Every AI workflow mints a `JacobPEvans-claude` GitHub App installation token immediately before calling the action, then hands it in as `github_token` with `use_commit_signing: true`. Commits land web-flow-signed and attributed to the bot. The App credentials (`GH_APP_CLAUDE_BOT_PRIVATE_KEY`, `GH_APP_CLAUDE_BOT_ID`) are distributed by `secrets-sync` to every repo in the `_github_app_repos` anchor.

## Where to go next

<CardGroup cols={2}>
<Card title="Getting started" icon="rocket" href="https://github.com/JacobPEvans/ai-workflows/blob/main/docs/GETTING_STARTED.md">
Caller templates for every workflow, with the correct permission blocks.
</Card>
<Card title="Patterns" icon="puzzle-piece" href="https://github.com/JacobPEvans/ai-workflows/blob/main/docs/PATTERNS.md">
The post-merge dispatch pattern, bot guards, and other recurring shapes.
</Card>
<Card title="Authentication" icon="key" href="https://github.com/JacobPEvans/ai-workflows/blob/main/docs/AUTHENTICATION.md">
Anthropic API key setup, cost controls, fallback providers, why not OAuth.
</Card>
<Card title="Verification" icon="check-double" href="https://github.com/JacobPEvans/ai-workflows/blob/main/docs/VERIFICATION.md">
The e2e runbook for checking a freshly-wired repo end to end.
</Card>
<Card title="Issue → PR pipeline on this repo" icon="route" href="/automation/issue-to-pr-pipeline">
Exactly which six callers are wired on `JacobPEvans/docs` and why.
</Card>
<Card title="Secret distribution" icon="lock" href="/security/secrets-sync">
How ANTHROPIC_API_KEY and the App credentials land on each consumer repo.
</Card>
</CardGroup>
103 changes: 103 additions & 0 deletions automation/claude-code-routines.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: "claude-code-routines"
description: "Six cron-scheduled Claude Code routines that watch the whole org without per-repo wiring. Pick up loose ends the event-triggered pipeline misses."
tier: 2
---

> Cron, not events. Org-wide, not per-repo. One configuration, scans everything.

[`JacobPEvans/claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines) ships six prompt files that run on schedule inside Anthropic's cloud-hosted Claude Code sandbox. They discover repos under `$GH_OWNER` via `gh search`, do their work using `gh` CLI calls, and report results to Slack via the official Slack MCP connector. There is no per-repo wiring — drop a repo into the org and the routines pick it up on the next run.

## The six routines

| Routine | Schedule (CT) | What it does |
| --- | --- | --- |
| Morning Briefing | Daily 5:00 AM | Read-only activity summary: new PR reviews, opened issues, CI failures, anything that needs attention |
| The Sentinel | Daily 12:33 AM | Param/secret audit; picks one PR to review; flags operator-specific patterns |
| The Custodian | Daily 2:00 AM | Weighted-random maintenance: stale branch cleanup, missing labels, repo health audit |
| Issue Solver | Daily 7:00 AM + 7:00 PM | Picks one open issue, takes it from triage → draft PR in six phases |
| Daily Polish | Daily 11:00 PM | Deep-clean one repo per day: README gaps, missing `.gitignore`, CI config, test coverage |
| Weekly Scorecard | Mondays 5:00 AM | Portfolio health scores: repo count, open-issue median, CI pass rate, test coverage % |

## How the Issue Solver picks work

The most ambitious routine. Six phases, all running in the same sandbox session:

<Steps>
<Step title="Discover">
Shell-only. `gh search issues` across `$GH_OWNER` for the last 90 days, open + unassigned. Scored via `jq` weights — `bug=+50`, `good-first-issue=+40`, `type:security=-40`, `type:breaking=-40`, recent-activity bonus.
</Step>
<Step title="Triage">
Claude Sonnet classifies the top 5 by solvability + complexity (trivial/small/medium/large).
</Step>
<Step title="Investigate">
A read-only subagent reads the affected code and locates the exact lines that need changes.
</Step>
<Step title="Implement">
Pre-flight secret scan (`grep -P` against known patterns). Creates a branch via `gh api repos/.../git/refs`. Pushes file changes via Contents API — the App installation token auto-signs every commit.
</Step>
<Step title="Verify">
Best-effort CI polling at 30-second intervals up to 5 minutes. Records the final `ci_status` (passed / failed / pending / none).
</Step>
<Step title="Submit">
Opens a **draft PR only** — never `--ready`, never auto-merge. If the issue is too complex, comments on the issue explaining what was tried and gives up cleanly.
</Step>
</Steps>

## How routines authenticate

Each routine runs in Anthropic's cloud sandbox with pre-installed `gh` CLI. Authentication is via environment variables set in the cloud routine configuration at [claude.ai/code/routines](https://claude.ai/code):

| Env var | Purpose |
| --- | --- |
| `GH_TOKEN` | GitHub PAT for `gh` CLI |
| `GH_OWNER` | The org or user to scan (e.g. `JacobPEvans`) |
| `GH_OWNERS` | The Sentinel uses this — a list of orgs to spot-check |

Slack output goes through the official Slack MCP connector. The full operator-setup walkthrough lives in [`docs/CLOUD_ROUTINES_AUTH.md`](https://github.com/JacobPEvans/claude-code-routines/blob/main/docs/CLOUD_ROUTINES_AUTH.md) in the source repo.

## Current deploy gotcha

The native GitHub Actions deploy (`.github/workflows/deploy-routines.yml`) is **disabled** because `anthropics/claude-code-action@v1` does not carry the organization UUID binding that the Routines API requires — `RemoteTrigger` calls fail with `Unable to resolve organization UUID`.

**Workaround in use today**: a local `deploy-routine-changes` skill (under `.claude/skills/` in the routines repo) drives `RemoteTrigger get/update/create` calls during interactive Claude Code sessions. The interactive harness has the org binding the GHA runner lacks. The fallback is the [`/schedule update`](https://claude.ai/code) CLI flow if `RemoteTrigger` breaks in interactive mode too.

When Anthropic fixes the OAuth token to carry org UUID, the workflow can be restored. Until then, deploys are manual + interactive.

## What routines do NOT do

| Won't | Why |
| --- | --- |
| Merge any PR | Same merge prohibition as `/finalize-pr` |
| Open non-draft PRs | Human review is always the gate |
| Cross org boundaries | Owner derived from current config only |
| Touch archived or fork repos | `gh search` filters them out |
| Run during incidents | No event-driven escape hatch — wait for next cron tick |

## Where this fits relative to ai-workflows

| If you want… | Use… |
| --- | --- |
| A PR draft within minutes of opening an issue | `ai-workflows/issue-resolver.yml` event-triggered caller |
| A daily sweep that catches issues the event flow missed | `claude-code-routines` Issue Solver |
| To audit health across the whole org | `claude-code-routines` Sentinel + Weekly Scorecard |
| To fix CI failure on a PR you're iterating on right now | `/ship` from a local Claude Code session |

The two systems are complementary — ai-workflows handles the per-event reaction, claude-code-routines handles the org-wide sweep.

## Where to go next

<CardGroup cols={2}>
<Card title="Source repo" icon="github" href="https://github.com/JacobPEvans/claude-code-routines">
The six prompt files, schedule manifest, deploy notes.
</Card>
<Card title="Cloud routine auth" icon="key" href="https://github.com/JacobPEvans/claude-code-routines/blob/main/docs/CLOUD_ROUTINES_AUTH.md">
Operator setup — env vars, Slack MCP, token strategy.
</Card>
<Card title="ai-workflows" icon="github" href="/automation/ai-workflows">
The event-driven half of the same ecosystem.
</Card>
<Card title="Issue → PR pipeline" icon="route" href="/automation/issue-to-pr-pipeline">
The per-repo cloud flow that runs in parallel.
</Card>
</CardGroup>
75 changes: 75 additions & 0 deletions automation/codeql-resolution.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: "CodeQL resolution"
description: "CodeQL alerts are a separate gate from CI checks. /resolve-codeql is the skill that closes them automatically so /ship can complete."
tier: 2
---

> CodeQL is not in `statusCheckRollup`. If you only check CI, you miss the CodeQL gate. `/resolve-codeql` fixes that.

CodeQL is GitHub's static-analysis security scanner. It runs on every PR via the `Analyze (actions)` or `Analyze (javascript)` jobs (depending on what's enabled for the repo). When it finds a violation, it posts a **code-scanning alert** that blocks merge — but **does not** show up in `gh pr checks` or in the `statusCheckRollup` of a `gh pr view --json` query. Branch protection treats the alert as a separate signal.

## Why this matters for `/ship`

If `/finalize-pr` only queried `statusCheckRollup`, a PR could look ready (`statusCheckRollup.state: SUCCESS`) and still be blocked at merge time by an open CodeQL alert. The skill's Phase 3 explicitly runs a second, separate gate for CodeQL — using the REST code-scanning API, not GraphQL.

```bash
gh api repos/<owner>/<repo>/code-scanning/alerts \
--jq '[.[] | select(.state == "open")] | length'
```

If the result is non-zero, the PR is **not** ready, regardless of what CI says.

## What `/resolve-codeql` does

`/resolve-codeql fix` is the auto-resolution skill invoked by `/finalize-pr` Phase 2 when alerts exist:

<Steps>
<Step title="Enumerate alerts">
Lists all open alerts for the repo, with rule ID, severity, and file:line.
</Step>
<Step title="Classify">
For each alert: is it a real vulnerability, an intentional pattern that should be suppressed, or a false positive?
</Step>
<Step title="Fix or suppress">
Real → patch the code with a fix that addresses the rule. Intentional → add a CodeQL inline suppression with a justification comment. False positive → dismiss via the API with `"reason": "false positive"`.
</Step>
<Step title="Push the fix commit">
Signed via the App installation token, attributed to `JacobPEvans-claude[bot]`.
</Step>
<Step title="Re-run CodeQL">
The push triggers a fresh CodeQL run automatically. Wait for it to complete before re-querying alert state.
</Step>
</Steps>

## Why GraphQL doesn't show CodeQL state

The GraphQL `pullRequest` schema's `statusCheckRollup` aggregates check runs and status contexts. CodeQL alerts live in a separate `repository.codeScanningAlerts` connection — they're attached to the repo's default branch baseline, not to the PR's check run set.

A canonical query for the PR-readiness gate (from [`gh-cli-patterns`](https://github.com/JacobPEvans/claude-code-plugins/blob/main/github-workflows/skills/gh-cli-patterns/SKILL.md)) reads `statusCheckRollup` for CI, then does a **separate** REST call for CodeQL. Both must be clean for a PR to be considered ready.

## Async timing

CodeQL runs are slower than most CI checks — often 2–5 minutes after a push. This timing mismatch is why `/finalize-pr` Phase 2.6 (the fix in flight) introduces an explicit wait-for-async-checks step: poll at 30-second intervals up to 5 minutes, only advance to Phase 3 when all known check kinds have a terminal status. Without this, `/finalize-pr` could treat a still-running CodeQL run as a hard failure and bail out.

## What `/resolve-codeql` will NOT do

- **Won't dismiss real alerts as false positive.** Dismissals require justification and are reserved for genuine FPs.
- **Won't disable a CodeQL query.** Adjusting the CodeQL config is a deliberate human change.
- **Won't loop indefinitely.** Capped at 3 fix attempts per PR per invocation — beyond that, surfaces the unfixed alerts for a human.

## Where to go next

<CardGroup cols={2}>
<Card title="/ship and /finalize-pr" icon="ship" href="/automation/ship-and-finalize">
The orchestrator that invokes `/resolve-codeql` as part of Phase 2.
</Card>
<Card title="claude-code-plugins" icon="plug" href="/ai-development/claude-code-plugins">
The plugin home for `/resolve-codeql` and the rest of the toolchain.
</Card>
<Card title="Source: codeql-resolver" icon="github" href="https://github.com/JacobPEvans/claude-code-plugins/tree/main/codeql-resolver">
The full plugin source.
</Card>
<Card title="GitHub CodeQL docs" icon="book" href="https://docs.github.com/en/code-security/code-scanning">
The upstream CodeQL documentation for query authoring and config.
</Card>
</CardGroup>
Loading
Loading