diff --git a/ai-development/claude-code-plugins.mdx b/ai-development/claude-code-plugins.mdx
index a480a4f..f0a0823 100644
--- a/ai-development/claude-code-plugins.mdx
+++ b/ai-development/claude-code-plugins.mdx
@@ -58,11 +58,14 @@ Workflow logic. Anything that's "how Claude Code should approach a task" lives h
The rules layer these plugins operate within.
-
+
+ The full skill lifecycle — phases, exit modes, known reliability gaps.
+
+
Scheduled Claude.ai routines — the cron side of the same ecosystem.
-
- The Copilot equivalent — reusable agentic workflows.
+
+ Reusable GitHub Actions workflows that drive the cloud half of the pipeline.
Plugins, hooks, full README.
diff --git a/architecture/ai-pipeline.mdx b/architecture/ai-pipeline.mdx
index f284263..fdc6ee3 100644
--- a/architecture/ai-pipeline.mdx
+++ b/architecture/ai-pipeline.mdx
@@ -108,7 +108,7 @@ Every other PAL tool has a native Claude Code equivalent; for single-model calls
-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
diff --git a/automation/ai-workflows.mdx b/automation/ai-workflows.mdx
new file mode 100644
index 0000000..fa8f677
--- /dev/null
+++ b/automation/ai-workflows.mdx
@@ -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
+
+
+
+ Caller templates for every workflow, with the correct permission blocks.
+
+
+ The post-merge dispatch pattern, bot guards, and other recurring shapes.
+
+
+ Anthropic API key setup, cost controls, fallback providers, why not OAuth.
+
+
+ The e2e runbook for checking a freshly-wired repo end to end.
+
+
+ Exactly which six callers are wired on `JacobPEvans/docs` and why.
+
+
+ How ANTHROPIC_API_KEY and the App credentials land on each consumer repo.
+
+
diff --git a/automation/claude-code-routines.mdx b/automation/claude-code-routines.mdx
new file mode 100644
index 0000000..714a007
--- /dev/null
+++ b/automation/claude-code-routines.mdx
@@ -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:
+
+
+
+ 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.
+
+
+ Claude Sonnet classifies the top 5 by solvability + complexity (trivial/small/medium/large).
+
+
+ A read-only subagent reads the affected code and locates the exact lines that need changes.
+
+
+ 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.
+
+
+ Best-effort CI polling at 30-second intervals up to 5 minutes. Records the final `ci_status` (passed / failed / pending / none).
+
+
+ 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.
+
+
+
+## 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
+
+
+
+ The six prompt files, schedule manifest, deploy notes.
+
+
+ Operator setup — env vars, Slack MCP, token strategy.
+
+
+ The event-driven half of the same ecosystem.
+
+
+ The per-repo cloud flow that runs in parallel.
+
+
diff --git a/automation/codeql-resolution.mdx b/automation/codeql-resolution.mdx
new file mode 100644
index 0000000..44877dd
--- /dev/null
+++ b/automation/codeql-resolution.mdx
@@ -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///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:
+
+
+
+ Lists all open alerts for the repo, with rule ID, severity, and file:line.
+
+
+ For each alert: is it a real vulnerability, an intentional pattern that should be suppressed, or a false positive?
+
+
+ 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"`.
+
+
+ Signed via the App installation token, attributed to `JacobPEvans-claude[bot]`.
+
+
+ The push triggers a fresh CodeQL run automatically. Wait for it to complete before re-querying alert state.
+
+
+
+## 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
+
+
+
+ The orchestrator that invokes `/resolve-codeql` as part of Phase 2.
+
+
+ The plugin home for `/resolve-codeql` and the rest of the toolchain.
+
+
+ The full plugin source.
+
+
+ The upstream CodeQL documentation for query authoring and config.
+
+
diff --git a/automation/issue-to-pr-pipeline.mdx b/automation/issue-to-pr-pipeline.mdx
new file mode 100644
index 0000000..4f0fa7a
--- /dev/null
+++ b/automation/issue-to-pr-pipeline.mdx
@@ -0,0 +1,129 @@
+---
+title: "Issue to PR pipeline"
+description: "The eight-step cloud flow that turns a new GitHub issue into a draft PR with green CI, AI review, and a merge-readiness comment."
+tier: 2
+---
+
+> Open issue. Don't open the dashboard. Come back in five minutes to a draft PR ready for your eyes.
+
+The pipeline on this repo, [`JacobPEvans/docs`](https://github.com/JacobPEvans/docs), uses six thin caller files in `.github/workflows/` that delegate to reusable workflows in [`JacobPEvans/ai-workflows`](https://github.com/JacobPEvans/ai-workflows). Each caller is 10–30 lines; the AI work all lives in the upstream reusable workflow.
+
+## What runs, in order
+
+| Step | Trigger | Caller file | Reusable workflow | What it does |
+| --- | --- | --- | --- | --- |
+| 1 | `issues: [opened]` | `issue-triage.yml` | `ai-workflows/.github/workflows/issue-triage.yml@main` | Categorizes, deduplicates, labels |
+| 2 | `issues: [opened]` | `issue-resolver.yml` | `ai-workflows/.github/workflows/issue-resolver.yml@main` | Creates a **draft PR** if the issue is well-scoped and not on the excluded-labels list |
+| 3 | `pull_request: opened/synchronize/ready_for_review` | (no caller — handled by upstream reviewers) | external Gemini + Copilot reviews | Posts inline review comments |
+| 4 | `workflow_run` on `CI`, `conclusion: failure` | `ci-fix.yml` | `ai-workflows/.github/workflows/ci-fix.yml@main` | Reads the failed CI log, pushes a fix commit (up to 2 attempts per PR, 5 per day) |
+| 5 | `pull_request_review: [submitted]` | `final-pr-review.yml` | `ai-workflows/.github/workflows/final-pr-review.yml@main` | Final merge-readiness gate — checks TODOs, debugger artifacts, CI rollup |
+| 6 | `issues: [opened, labeled]`, `pull_request: [opened, ready_for_review]` | `project-router.yml` | `ai-workflows/.github/workflows/project-router.yml@main` | Routes the item to the right GitHub Project with smart field assignment |
+| 7 | `push: [main]` → re-dispatched as `workflow_dispatch` | `post-merge-docs-review.yml` | `ai-workflows/.github/workflows/post-merge-docs-review.yml@main` | After merge, audits docs touched by the commit and creates fix PRs if needed |
+| 8 | Human clicks **Merge** | n/a | n/a | The only manual step |
+
+The deprecated `claude-review.yml` (removed 2026-04-04 in favor of Gemini + Copilot reviews) and the disabled-auto-trigger `pr-issue-linker.yml` are not wired. See [ai-workflows](/automation/ai-workflows) for the full catalog and what's not active.
+
+## How the six callers connect
+
+{/* Shape: hub-and-spokes. 1 hub, 6 leaves stacked via invisible links. */}
+{/* Aspect: ~4:3 (LR). Pass. */}
+
+```mermaid
+%%{init: {'theme':'base','look':'handDrawn','themeVariables':{'fontFamily':'Geist','fontSize':'14px','primaryColor':'#102937','primaryTextColor':'#F4EFE6','primaryBorderColor':'#4FB3A9','lineColor':'#4FB3A9','secondaryColor':'#0B1D2A','tertiaryColor':'#1A2A38','clusterBkg':'rgba(79,179,169,0.08)','clusterBorder':'#4FB3A9'}}}%%
+flowchart LR
+ R([docs repo])
+
+ subgraph callers[Caller files in .github/workflows/]
+ direction TB
+ A([issue-triage])
+ B([issue-resolver])
+ C([ci-fix])
+ D([final-pr-review])
+ E([project-router])
+ F([post-merge-docs-review])
+ A ~~~ B ~~~ C ~~~ D ~~~ E ~~~ F
+ end
+
+ R --> A
+
+ classDef host fill:#102937,stroke:#4FB3A9,stroke-width:2px,color:#F4EFE6;
+ classDef ai fill:#102937,stroke:#E06B4A,stroke-width:2px,color:#F4EFE6;
+
+ class R host
+ class A,B,C,D,E,F ai
+```
+
+The hub is the consumer repo; each caller is a one-shot wrapper around an upstream reusable workflow. They run independently when their trigger fires.
+
+## What each caller actually contains
+
+A caller is the minimum YAML to declare a trigger, set permissions, and call 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
+```
+
+Two patterns are slightly larger:
+
+- **`ci-fix.yml`** passes a `repo_context` and `ci_structure` describing what the repo is and what CI runs, so the AI knows what to fix.
+- **`post-merge-docs-review.yml`** uses the [Post-Merge Dispatch Pattern](https://github.com/JacobPEvans/ai-workflows/blob/main/docs/PATTERNS.md#post-merge-dispatch-pattern) — a two-job file because `push` events aren't supported by `claude-code-action@v1`, so the caller re-dispatches as `workflow_dispatch`.
+
+## Secrets the pipeline needs
+
+Distributed automatically by [`secrets-sync`](/security/secrets-sync) when a repo is added to the `_github_app_repos` and `_all_repos` anchors in `secrets-config.yml`:
+
+| Secret / variable | Source | Purpose |
+| --- | --- | --- |
+| `ANTHROPIC_API_KEY` (secret) | `_all_repos` | Auth for `claude-code-action` to call the Anthropic API directly |
+| `GH_APP_CLAUDE_BOT_PRIVATE_KEY` (secret) | `_github_app_repos` | Mints App tokens for signed commits attributed to `JacobPEvans-claude[bot]` |
+| `GH_APP_CLAUDE_BOT_ID` (variable) | `_github_app_repos` | App identifier |
+
+Per [`git-signing.md`](https://github.com/JacobPEvans/ai-assistant-instructions/blob/main/agentsmd/rules/git-signing.md), every AI workflow mints a `JacobPEvans-claude` installation token immediately before calling `claude-code-action@v1`, then hands it in as `github_token` with `use_commit_signing: true`. Commits land web-flow-signed and attributed to the bot.
+
+## Rate and safety guards
+
+The reusable workflows enforce conservative defaults so a runaway loop can't burn cloud spend:
+
+- **`issue-resolver.yml`** — `max_attempts: 1` per issue, `daily_limit: 5` per repo, `excluded_labels: "type:security,type:feature,type:breaking,size:l,size:xl"` won't touch
+- **`ci-fix.yml`** — `daily_run_limit: 5` per repo, max 2 fix attempts per PR
+- **`final-pr-review.yml`** — `daily_run_limit: 5`
+- **All workflows** — fork PRs blocked by `if:` guards, branch protection enforces the final merge gate
+
+## The pieces this doesn't include
+
+The cloud pipeline gets a PR to **draft + reviewed**. It does NOT:
+
+- Mark the PR ready for merge — that's a human decision
+- Click the merge button — never automated
+- Override branch protection or required reviewers
+- Touch repos outside the current org boundary
+
+For the local iteration loop on a PR you're editing yourself, see [`/ship` and `/finalize-pr`](/automation/ship-and-finalize).
+
+## Where to go next
+
+
+
+ The full catalog of 16 reusable workflows, event-triggered + scheduled.
+
+
+ The cron half — six routines that scan the org and pick up loose ends.
+
+
+ Caller templates and the live workflow catalog.
+
+
+ Why direct Anthropic API, not OAuth tokens; cost controls; fallback providers.
+
+
diff --git a/automation/overview.mdx b/automation/overview.mdx
new file mode 100644
index 0000000..98618bc
--- /dev/null
+++ b/automation/overview.mdx
@@ -0,0 +1,76 @@
+---
+title: "Automation"
+description: "How an issue becomes a merged PR with minimal human touch: ai-workflows in the cloud, claude-code-routines on cron, claude-code-plugins on the laptop."
+tier: 1
+---
+
+> File an issue. Walk away. Come back to a draft PR with green CI, AI review, and one human merge click left.
+
+This is the **issue → mergeable PR** pipeline. Three repos collaborate: `ai-workflows` runs on GitHub Actions per consumer repo, `claude-code-routines` runs on Anthropic's cloud cron, `claude-code-plugins` is the local Claude Code escape hatch.
+
+## The end-to-end shape
+
+{/* Shape: linear chain. Boundary crossings: 0. Ranks: 6×1. */}
+{/* Aspect: ~2.5:1 (LR). Pass. */}
+
+```mermaid
+%%{init: {'theme':'base','look':'handDrawn','themeVariables':{'fontFamily':'Geist','fontSize':'14px','primaryColor':'#102937','primaryTextColor':'#F4EFE6','primaryBorderColor':'#4FB3A9','lineColor':'#4FB3A9','secondaryColor':'#0B1D2A','tertiaryColor':'#1A2A38','clusterBkg':'rgba(79,179,169,0.08)','clusterBorder':'#4FB3A9'}}}%%
+flowchart LR
+ I([Issue])
+ T([AI triage])
+ D([Draft PR])
+ L([CI and review loop])
+ G{Final gate}
+ M([Merged])
+
+ I --> T --> D --> L --> G --> M
+
+ classDef src fill:#102937,stroke:#E06B4A,stroke-width:2px,color:#F4EFE6;
+ classDef ai fill:#102937,stroke:#E06B4A,stroke-width:2px,color:#F4EFE6;
+ classDef auto fill:#102937,stroke:#F4EFE6,stroke-width:1.5px,color:#F4EFE6;
+ classDef gate fill:#102937,stroke:#E06B4A,stroke-width:2.5px,color:#F4EFE6;
+ classDef sink fill:#102937,stroke:#F4EFE6,stroke-width:2px,color:#F4EFE6;
+
+ class I src
+ class T,L ai
+ class D auto
+ class G gate
+ class M sink
+```
+
+A human files the issue and clicks merge. Everything in between runs without supervision.
+
+## The three layers
+
+| Layer | Where it lives | When it runs | What it owns |
+| --- | --- | --- | --- |
+| [ai-workflows](/automation/ai-workflows) | GitHub Actions (per consumer repo) | Event-triggered (issues, PRs, CI failures) | Triage, draft PR creation, CI auto-fix, final review gate, project routing |
+| [claude-code-routines](/automation/claude-code-routines) | Anthropic cloud cron | Scheduled (cron 2×/day to weekly) | Org-wide maintenance: daily polish, issue solver, sentinel audit, weekly scorecard |
+| [claude-code-plugins](/ai-development/claude-code-plugins) | Your laptop | Interactive (`/ship` from any Claude Code session) | The local escape hatch — finalize a PR you're iterating on |
+
+## The six moving parts
+
+
+
+ The eight-step cloud flow on a target repo. Which trigger fires which file, who reviews, what gates merge.
+
+
+ The 16 reusable workflows behind the cloud pipeline. Event-triggered and scheduled.
+
+
+ Six cron-scheduled Claude routines. Org-wide maintenance, no per-repo wiring.
+
+
+ The local skill that closes the loop on a PR you're iterating on. Phases, exit modes, the known reliability gaps.
+
+
+ Why CodeQL is fixed separately from CI checks, and how `/resolve-codeql` plugs in.
+
+
+ The `/shape-issues` skill that turns rough ideas into well-scoped issues the resolver can act on.
+
+
+
+## Source repos
+
+[`ai-workflows`](https://github.com/JacobPEvans/ai-workflows) · [`claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines) · [`claude-code-plugins`](https://github.com/JacobPEvans/claude-code-plugins). The 30-second pitch lives at [AI pipeline](/architecture/ai-pipeline); this Automation section is the mechanics.
diff --git a/automation/shape-issues.mdx b/automation/shape-issues.mdx
new file mode 100644
index 0000000..6e0cd04
--- /dev/null
+++ b/automation/shape-issues.mdx
@@ -0,0 +1,94 @@
+---
+title: "Shape issues"
+description: "Raw ideas become well-scoped issues the resolver can act on. The /shape-issues skill applies Shape Up methodology to GitHub issues before they hit the pipeline."
+tier: 2
+---
+
+> Rough idea in. Clear problem statement, appetite, scope boundaries, and acceptance criteria out.
+
+`/shape-issues` is a local Claude Code skill that takes raw issue text — a one-line note, a Slack quote, a brain dump — and reshapes it into a structured GitHub issue. The reshaped issue carries enough scope clarity that `issue-resolver.yml` in [`ai-workflows`](/automation/ai-workflows) can draft a sensible PR against it, instead of stalling on ambiguity.
+
+## Why shaping matters
+
+[`issue-resolver.yml`](https://github.com/JacobPEvans/ai-workflows/blob/main/.github/workflows/issue-resolver.yml) is conservative by design. It refuses to draft PRs for issues labeled `type:security`, `type:feature`, `type:breaking`, or `size:l/xl` — the situations where AI judgment carries too much risk. Issues that aren't labeled at all default to "don't act."
+
+That means the difference between an issue that gets a PR and one that sits is often **how the issue is written**, not how clever the AI is. A vague "improve docs structure" stays untouched. A well-shaped "add a `Comparison` table to `infrastructure/lxc-vs-docker.mdx` with columns Memory / Disk / Network / Startup time, fed from the Proxmox cluster benchmark notes" gets a draft PR in five minutes.
+
+## What Shape Up gives you
+
+[Shape Up](https://basecamp.com/shapeup) is Basecamp's product-shaping methodology. The relevant elements for `/shape-issues`:
+
+| Concept | What it means here |
+| --- | --- |
+| **Problem** | One sentence — the real thing that needs solving, not the symptom |
+| **Appetite** | How much time/risk this is worth (1 day, 1 week, 6 weeks) — gates whether the issue should even be opened |
+| **Solution sketch** | A 2–4 bullet outline of the approach — enough that a reasonable agent can act, vague enough to leave room for judgment |
+| **Rabbit holes** | Things the implementer might fall into and burn time on — explicitly out of scope |
+| **No-goes** | What this issue **will not** address (so a follow-up issue can hold those instead) |
+
+## What the skill outputs
+
+`/shape-issues` rewrites a draft issue body into this shape:
+
+```markdown
+## Problem
+One sentence stating the actual pain or gap.
+
+## Appetite
+1 day | 1 week | 6 weeks
+
+## Solution sketch
+- Bullet 1: the highest-leverage change
+- Bullet 2: the supporting change
+- Bullet 3: validation step
+
+## Rabbit holes
+- Thing you'll be tempted to rewrite, but don't
+- Adjacent system that's broken but out of scope
+
+## No-goes
+- Explicit non-goals
+```
+
+Plus a recommended label set (`size:s`, `type:enhancement`, etc.) based on appetite + scope.
+
+## How `/shape-issues` connects to the pipeline
+
+```text
+Raw idea
+ ↓
+/shape-issues (local)
+ ↓
+gh issue create
+ ↓
+ai-workflows/issue-triage.yml ← applies labels
+ai-workflows/issue-resolver.yml ← drafts a PR (or skips with reason)
+ ↓
+Cloud pipeline takes over from here
+```
+
+The skill itself doesn't open the issue — you review the shaped output, then `gh issue create --title ... --body ...`. The triage workflow takes over once GitHub fires the `issues: [opened]` event.
+
+## When NOT to use `/shape-issues`
+
+- **Bug reports with a clear reproduction.** A trace and a stack speak for themselves; shaping adds noise.
+- **One-line typo fixes.** Just open the issue.
+- **Anything with `type:security` label.** Security issues are handled outside the resolver pipeline; shape them differently.
+- **Major architectural decisions.** Those need an [RFC](https://github.com/JacobPEvans/ai-assistant-instructions) or a design doc, not an issue.
+
+## Where to go next
+
+
+
+ The downstream consumer — what `/shape-issues` is preparing input for.
+
+
+ What happens after a shaped issue is opened.
+
+
+ The full methodology Basecamp open-sourced.
+
+
+ The plugin home for `/shape-issues`.
+
+
diff --git a/automation/ship-and-finalize.mdx b/automation/ship-and-finalize.mdx
new file mode 100644
index 0000000..8db2dc8
--- /dev/null
+++ b/automation/ship-and-finalize.mdx
@@ -0,0 +1,126 @@
+---
+title: "/ship and /finalize-pr"
+description: "The local Claude Code skill that drives a PR to a fully-mergeable state. Phases, exit modes, and the reliability gaps being closed."
+tier: 2
+---
+
+> One command. Commit, push, open the PR, fix CodeQL, resolve threads, fix CI, verify everything is green. Never merges. That's a human decision.
+
+`/ship` is the local escape hatch for the cloud pipeline. The cloud pipeline ([ai-workflows](/automation/ai-workflows), [claude-code-routines](/automation/claude-code-routines)) gets a PR opened and reviewed without you. `/ship` is what you run from a Claude Code session when **you** are the one iterating on a PR and want it driven to a ready-to-merge state in one command.
+
+## What `/ship` does
+
+`/ship` is a thin orchestrator. It detects uncommitted changes (commits them), discovers recently-created PRs, then for each PR invokes `/finalize-pr` — which is where the real work happens.
+
+```text
+/ship
+ ├─ Step 0: verify git repo
+ ├─ Step 1: commit any uncommitted changes; discover PRs in scope
+ ├─ Step 1.5: build a context brief from the conversation
+ ├─ Step 2: invoke /finalize-pr per PR, sequentially
+ └─ Step 3: re-verify each PR with live GitHub state before reporting
+```
+
+## What `/finalize-pr` does
+
+`/finalize-pr` is the loop. Five phases per PR:
+
+{/* Shape: linear chain. Boundary crossings: 0. Ranks: 5×1. */}
+{/* Aspect: ~3:1 (LR). Pass. */}
+
+```mermaid
+%%{init: {'theme':'base','look':'handDrawn','themeVariables':{'fontFamily':'Geist','fontSize':'14px','primaryColor':'#102937','primaryTextColor':'#F4EFE6','primaryBorderColor':'#4FB3A9','lineColor':'#4FB3A9','secondaryColor':'#0B1D2A','tertiaryColor':'#1A2A38','clusterBkg':'rgba(79,179,169,0.08)','clusterBorder':'#4FB3A9'}}}%%
+flowchart LR
+ P1([1. Discover])
+ P2([2. Resolve loop])
+ P3{3. Verify gate}
+ P4([4. Update metadata])
+ P5([5. Report ready])
+
+ P1 --> P2 --> P3 --> P4 --> P5
+ P3 -.->|gate fails| P2
+
+ classDef ai fill:#102937,stroke:#E06B4A,stroke-width:2px,color:#F4EFE6;
+ classDef auto fill:#102937,stroke:#F4EFE6,stroke-width:1.5px,color:#F4EFE6;
+ classDef gate fill:#102937,stroke:#E06B4A,stroke-width:2.5px,color:#F4EFE6;
+
+ class P1,P2,P4 ai
+ class P5 auto
+ class P3 gate
+
+ linkStyle 0,1,2,3 stroke:#4FB3A9,stroke-width:2px;
+ linkStyle 4 stroke:#E06B4A,stroke-width:1.5px,stroke-dasharray:4 3;
+```
+
+| Phase | What it owns |
+| --- | --- |
+| 1. Discover | Resolve which PR(s) to act on (current branch, all, or org-wide) |
+| 2. Resolve loop | Start CI monitor in background; in parallel: invoke `/resolve-codeql fix`, `/resolve-pr-threads`, merge-conflict resolution; once CI completes, run `/simplify` once on the cumulative changes |
+| 3. Verify gate | Re-query live GitHub state — `state`, `mergeable`, `mergeStateStatus`, `reviewDecision`, `statusCheckRollup.state`, all `reviewThreads.isResolved`, CodeQL alert count |
+| 4. Update metadata | A subagent updates PR title, description, linked issues |
+| 5. Report ready | Emit the canonical PR status block; wait for human merge |
+
+## What it never does
+
+- **Never merges.** Merge is always a human decision.
+- **Never approves.** No auto-approval of PRs.
+- **Never crosses org boundaries** in org-wide mode.
+- **Never bypasses branch protection.**
+
+## Subagents in the call chain
+
+```text
+/ship → /finalize-pr → /resolve-codeql fix
+ → /resolve-pr-threads → superpowers:receiving-code-review
+ → /simplify
+ → haiku subagent (PR metadata update)
+ → background CI monitor (Task tool)
+```
+
+Each subagent runs scoped to its task and reports back. The orchestrator re-verifies live state — never trusts a subagent's self-report as ground truth.
+
+## Known reliability gaps
+
+`/ship` has been ending prematurely without leaving PRs fully mergeable. Root causes traced to the current `SKILL.md` files:
+
+| Failure mode | Why it happens |
+| --- | --- |
+| Exits "blocked" when a fix could have continued | Phase 3 → Phase 2 loop is prose with no enforced iteration counter — subagents treat it as advisory |
+| Treats async-pending checks as failures | Phase 3 aborts on `statusCheckRollup.state ≠ SUCCESS`, but Phase 2 can't fix a pending check |
+| Silent subagent failure passes through | After `/resolve-codeql` and `/resolve-pr-threads`, skill advances without re-querying state to confirm fixes landed |
+| CI monitor dies silently | Background Task agent can fail without propagating |
+| All Phase 3 aborts treated identically | Some failures are agent-fixable, some are wait, some require humans (`REVIEW_REQUIRED`) |
+
+Fix in flight: explicit iteration counter (cap 5), wait-for-async-checks phase, post-fix verification of subagent work, CI-monitor failure fallback, and a failure-mode taxonomy that maps each `mergeStateStatus`/`reviewDecision` to a handler. After the fix, every `/ship` invocation reports one of three categories with a specific reason — never silent half-finalization:
+
+- **Ready to merge** — all gates clean
+- **Ready except human gate** — only `REVIEW_REQUIRED` remaining
+- **Ship aborted** — specific gate stuck with manual-action suggestion
+
+Tracked as a PR against [`claude-code-plugins`](https://github.com/JacobPEvans/claude-code-plugins).
+
+## When to use `/ship` vs the cloud pipeline
+
+| Situation | Use |
+| --- | --- |
+| Issue is filed, you want a PR drafted without thinking | [`ai-workflows`](/automation/ai-workflows) (the cloud pipeline triggered on issue open) |
+| You're editing a PR locally and want CI/threads/CodeQL handled in one command | `/ship` from your Claude Code session |
+| You want a daily org-wide sweep | [`claude-code-routines`](/automation/claude-code-routines) (cron-scheduled) |
+| You want to know if a PR is *really* ready to merge | `/finalize-pr ` directly — gives you the canonical gate output without re-running fixes |
+
+## Where to go next
+
+
+
+ Why CodeQL is fixed separately from CI checks, and what `/resolve-codeql` does.
+
+
+ The plugin home for `/ship`, `/finalize-pr`, and all the supporting skills.
+
+
+ The full skill definition.
+
+
+ Phases, gates, and the merge prohibition.
+
+
diff --git a/docs.json b/docs.json
index 5b4addd..9d22e4d 100644
--- a/docs.json
+++ b/docs.json
@@ -134,6 +134,18 @@
"ai-development/claude-code-plugins"
]
},
+ {
+ "group": "Automation",
+ "pages": [
+ "automation/overview",
+ "automation/issue-to-pr-pipeline",
+ "automation/ai-workflows",
+ "automation/claude-code-routines",
+ "automation/ship-and-finalize",
+ "automation/codeql-resolution",
+ "automation/shape-issues"
+ ]
+ },
{
"group": "Conventions",
"pages": [
diff --git a/tools/automation.mdx b/tools/automation.mdx
index 9591952..34f27de 100644
--- a/tools/automation.mdx
+++ b/tools/automation.mdx
@@ -77,4 +77,10 @@ These five skills are filed as GitHub issues and tracked in the `enhancement` +
The broader plugin ecosystem this skill draws from.
+
+ The other half of the automation story — the issue → mergeable PR pipeline that runs across every JacobPEvans repo.
+
+
+ The Claude Code skill that takes a PR through CI, CodeQL, threads, and the final merge gate.
+