diff --git a/ai-development/claude-code-plugins.mdx b/ai-development/claude-code-plugins.mdx
index a480a4f..c665fe3 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/ai-development/skills/codeql-resolution.mdx b/ai-development/skills/codeql-resolution.mdx
new file mode 100644
index 0000000..4ec23cd
--- /dev/null
+++ b/ai-development/skills/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/ai-development/skills/overview.mdx b/ai-development/skills/overview.mdx
new file mode 100644
index 0000000..d456c11
--- /dev/null
+++ b/ai-development/skills/overview.mdx
@@ -0,0 +1,37 @@
+---
+title: "Skills"
+description: "Local Claude Code slash-commands that pick up where the cloud pipeline stops. Use them from a session when you are the one iterating on a PR."
+tier: 1
+---
+
+> Cloud pipelines get a PR opened, reviewed, and CI'd without you. Skills are what you run from a Claude Code session when you are the one driving a PR to merge.
+
+A **skill** in this catalog is a slash-command shipped by [`JacobPEvans/claude-code-plugins`](/ai-development/claude-code-plugins). The plugin repo carries the source — each skill is a folder with `SKILL.md`, prompts, and supporting scripts; this section carries the human-readable narrative for the load-bearing ones.
+
+Skills are the local-execution counterpart to the [cloud pipelines](/automation/cloud-pipelines/overview) and [scheduled routines](/automation/scheduled-routines/overview). When the cloud finishes the job, you do not run a skill. When the cloud opens a draft PR but cannot drive it all the way (CodeQL alert needs human triage, review threads need negotiation, CI flake needs a retry strategy), you `/ship` it from a session.
+
+## What is documented here
+
+| Skill | Use when |
+| --- | --- |
+| [`/ship` and `/finalize-pr`](/ai-development/skills/ship-and-finalize) | You have a PR open and want it driven to a fully-mergeable state in one command |
+| [`/resolve-codeql`](/ai-development/skills/codeql-resolution) | A PR is blocked on a CodeQL alert that does not show up in `gh pr checks` |
+
+More skills exist in `claude-code-plugins`; only the ones that participate in the issue → mergeable PR flow are written up here. The full catalog (commands, hooks, agents, skills) lives on the [claude-code-plugins page](/ai-development/claude-code-plugins).
+
+## Where to go next
+
+
+
+ The source repo and full plugin catalog.
+
+
+ The event-triggered half — what skills do not have to do because the cloud already handled it.
+
+
+ Where each skill plugs into the broader flow.
+
+
+ The voice and commit-shape rules every skill applies before pushing.
+
+
diff --git a/ai-development/skills/ship-and-finalize.mdx b/ai-development/skills/ship-and-finalize.mdx
new file mode 100644
index 0000000..7c3aab9
--- /dev/null
+++ b/ai-development/skills/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/cloud-pipelines/ai-workflows), [claude-code-routines](/automation/scheduled-routines/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/cloud-pipelines/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/scheduled-routines/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/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/cloud-pipelines/ai-workflows.mdx b/automation/cloud-pipelines/ai-workflows.mdx
new file mode 100644
index 0000000..d939855
--- /dev/null
+++ b/automation/cloud-pipelines/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 the [CI/CD policy](/infrastructure/cicd/policy#dependency-versioning), 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 included monthly automation credits on the Claude subscription that backs these workflows.
+
+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/cloud-pipelines/overview.mdx b/automation/cloud-pipelines/overview.mdx
new file mode 100644
index 0000000..0d16775
--- /dev/null
+++ b/automation/cloud-pipelines/overview.mdx
@@ -0,0 +1,39 @@
+---
+title: "Cloud pipelines"
+description: "Event-triggered automation that runs in GitHub Actions per consumer repo. Issues become draft PRs, CI failures get fixed, merge readiness is gated."
+tier: 1
+---
+
+> Event in (`issues.opened`, `workflow_run`, `pull_request`). PR out. Every step runs on GitHub-hosted runners or RunsOn spot, attributable to the AI-bot App identity.
+
+Cloud pipelines are the **event-driven** half of the automation surface. Something happens in GitHub (an issue is filed, a CI run fails, a PR is opened), the reusable workflows in [`JacobPEvans/ai-workflows`](/automation/cloud-pipelines/ai-workflows) react, and the result lands as a draft PR or a commit on an existing PR — signed by [`JacobPEvans-claude[bot]`](/infrastructure/cicd/git-signing) and ready for review.
+
+The cron-driven sibling — work that runs on a schedule rather than in response to an event — is documented under [Scheduled routines](/automation/scheduled-routines/overview).
+
+## How they fit
+
+| Trigger | Reusable workflow | Outcome |
+| --- | --- | --- |
+| `issues.opened` (label: `agentic-implement`) | `issue-resolver.yml` | Draft PR implementing the issue |
+| `workflow_run` on `CI`, `conclusion: failure` | `ci-fix.yml` | Fix commit pushed back to the failing PR |
+| `pull_request` opened or `synchronize` | `code-simplifier.yml`, `final-pr-review.yml` | Review comments + an auto-merge nudge when ready |
+| `gh-aw` action SHA stale (Mon/Thu schedule) | `gh-aw-pin-refresh.yml` | PR refreshing pinned action SHAs |
+
+The full caller catalog and the post-merge dispatch chain live on the [ai-workflows page](/automation/cloud-pipelines/ai-workflows).
+
+## Where to go next
+
+
+
+ Sixteen reusable workflows, the consumer-caller shape, auth + signing.
+
+
+ The cron-driven sibling. Org-wide, no per-repo wiring.
+
+
+ The end-to-end flow on `JacobPEvans/docs`, step by step.
+
+
+ How AI-authored commits land verified under the `JacobPEvans-claude[bot]` identity.
+
+
diff --git a/automation/issue-to-pr-pipeline.mdx b/automation/issue-to-pr-pipeline.mdx
new file mode 100644
index 0000000..a191384
--- /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/cloud-pipelines/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](/infrastructure/cicd/git-signing), 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`](/ai-development/skills/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..91cf046
--- /dev/null
+++ b/automation/overview.mdx
@@ -0,0 +1,70 @@
+---
+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 |
+| --- | --- | --- | --- |
+| [Cloud pipelines](/automation/cloud-pipelines/overview) | GitHub Actions (per consumer repo) | Event-triggered (issues, PRs, CI failures) | Triage, draft PR creation, CI auto-fix, final review gate, project routing |
+| [Scheduled routines](/automation/scheduled-routines/overview) | Anthropic cloud cron | Scheduled (cron 2×/day to weekly) | Org-wide maintenance: daily polish, issue solver, sentinel audit, weekly scorecard |
+| [Skills](/ai-development/skills/overview) | Your laptop | Interactive (`/ship` from any Claude Code session) | The local escape hatch — finalize a PR you're iterating on |
+
+## Where to go next
+
+
+
+ The eight-step cloud flow on a target repo. Which trigger fires which file, who reviews, what gates merge.
+
+
+ The 16 event-triggered reusable workflows behind the per-repo half of the automation surface.
+
+
+ Cron-scheduled Claude routines. Org-wide maintenance, no per-repo wiring.
+
+
+ Local Claude Code slash-commands — `/ship`, `/resolve-codeql` — that close the loop on a PR you are iterating 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/scheduled-routines/claude-code-routines.mdx b/automation/scheduled-routines/claude-code-routines.mdx
new file mode 100644
index 0000000..8542565
--- /dev/null
+++ b/automation/scheduled-routines/claude-code-routines.mdx
@@ -0,0 +1,105 @@
+---
+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.
+
+The local Claude Code that develops and deploys these routines is configured declaratively via [`nix-claude-code`](/nix/nix-claude-code) — plugin set, MCP wiring, permissions, and hooks all version-pinned in Nix.
+
+## 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/scheduled-routines/overview.mdx b/automation/scheduled-routines/overview.mdx
new file mode 100644
index 0000000..db988b0
--- /dev/null
+++ b/automation/scheduled-routines/overview.mdx
@@ -0,0 +1,38 @@
+---
+title: "Scheduled routines"
+description: "Cron-driven Claude Code sessions that watch the whole org. Pick up loose ends the event-triggered pipeline misses."
+tier: 1
+---
+
+> Time in (cron). Org-wide scan out. One configuration, every repo under `$GH_OWNER`, no per-repo wiring.
+
+Scheduled routines are the **cron-driven** half of the automation surface. The event-triggered cloud pipelines react to things that already happened in a single repo; the routines run on a schedule, scan the whole org, and pick up loose ends — stale READMEs, missing `.gitignore` rules, unresolved review threads, weekly summaries — that nothing else watches for.
+
+The event-driven sibling is documented under [Cloud pipelines](/automation/cloud-pipelines/overview).
+
+## What runs on a schedule
+
+The routines live in [`JacobPEvans/claude-code-routines`](/automation/scheduled-routines/claude-code-routines) as a small set of prompt files. Each one runs inside Anthropic's cloud-hosted Claude Code sandbox on its own schedule, discovers repos via `gh search`, does its work using `gh` CLI calls, and reports results to Slack.
+
+There is no per-repo wiring. Add a repo to the org and the routines pick it up on the next scheduled run.
+
+## How the install is managed
+
+The local Claude Code that runs these routines is configured declaratively in Nix via [`dryvist/nix-claude-code`](/nix/nix-claude-code) — plugins, marketplaces, skills, hooks, MCP servers, and permissions as composable home-manager modules. Reproducible across machines, rolled back with one command.
+
+## Where to go next
+
+
+
+ The routine catalog, schedules, and operator setup.
+
+
+ Declarative Claude Code via home-manager modules. How the routine host stays reproducible.
+
+
+ The event-triggered sibling that handles per-repo issues and CI failures.
+
+
+ Where routines fit relative to the rest of the issue-to-PR flow.
+
+
diff --git a/docs.json b/docs.json
index 5b4addd..e9fdbfc 100644
--- a/docs.json
+++ b/docs.json
@@ -122,6 +122,7 @@
"nix/nix-home",
"nix/nix-ai",
"nix/nix-devenv",
+ "nix/nix-claude-code",
"nix/package-placement"
]
},
@@ -131,7 +132,36 @@
"ai-development/overview",
"ai-development/repo-boundaries",
"ai-development/ai-assistant-instructions",
- "ai-development/claude-code-plugins"
+ "ai-development/claude-code-plugins",
+ {
+ "group": "Skills",
+ "pages": [
+ "ai-development/skills/overview",
+ "ai-development/skills/ship-and-finalize",
+ "ai-development/skills/codeql-resolution"
+ ]
+ }
+ ]
+ },
+ {
+ "group": "Automation",
+ "pages": [
+ "automation/overview",
+ "automation/issue-to-pr-pipeline",
+ {
+ "group": "Cloud pipelines",
+ "pages": [
+ "automation/cloud-pipelines/overview",
+ "automation/cloud-pipelines/ai-workflows"
+ ]
+ },
+ {
+ "group": "Scheduled routines",
+ "pages": [
+ "automation/scheduled-routines/overview",
+ "automation/scheduled-routines/claude-code-routines"
+ ]
+ }
]
},
{
diff --git a/nix/nix-claude-code.mdx b/nix/nix-claude-code.mdx
new file mode 100644
index 0000000..e12d7c5
--- /dev/null
+++ b/nix/nix-claude-code.mdx
@@ -0,0 +1,79 @@
+---
+title: "Declarative Claude Code"
+description: "Claude Code as composable home-manager modules. Plugins, marketplaces, skills, hooks, MCP, and permissions declared in Nix and rolled back atomically."
+tier: 2
+---
+
+import { RepoMeta, RepoFit } from "/snippets/repo-summary.mdx";
+
+> Declare the Claude Code you want. Apply. Roll it back with one command. Reproduce it on every machine.
+
+
+
+`nix-claude-code` ships [Claude Code](https://www.anthropic.com/claude-code) as composable home-manager modules. Marketplaces, plugins, statusline themes, hooks, MCP servers, and permission rules are all declared as Nix data — version-pinned in `flake.lock`, atomically rolled back with `darwin-rebuild --rollback`, identical across macOS and Linux.
+
+## What you get
+
+- **Every marketplace, wired up.** Anthropic's official marketplaces (`claude-plugins-official`, `anthropic-agent-skills`) plus 15+ curated community marketplaces, refreshing automatically.
+- **Plugins as data.** Every plugin you enable lands in `programs.claude.enabledPlugins`, pinned by `flake.lock`, rolled back with the rest of your generation.
+- **Permissions you can read.** Auto-approved tools, deny lists, and the WebFetch allowlist as structured Nix data — the same data feeds Codex, Gemini, and any other AI agent you wire up.
+- **Statusline themes.** Pick `powerline`, `ccstatusline`, or `daniel3303` with a single option.
+- **MCP plumbing.** Surface `programs.claude.mcpServers` from your favorite MCP runtime; they wire into Claude's `settings.json` exactly as the [official plugin reference](https://code.claude.com/docs/en/plugins-reference) specifies.
+- **Built on Anthropic's spec.** Reads `.claude-plugin/marketplace.json` and `.claude-plugin/plugin.json` per the upstream contract — no proprietary formats.
+
+## How it fits
+
+| Feeds into | Consumes |
+| --- | --- |
+| [User environment](/nix/nix-home) (home.packages), [AI tooling](/nix/nix-ai) overlay, scheduled-routine host | [ai-assistant-instructions](/ai-development/ai-assistant-instructions) rule set, [claude-code-plugins](/ai-development/claude-code-plugins) marketplace |
+
+
+The declarative path to a Claude Code install. If you want a single fully-configured Claude Code that survives a workstation wipe, this is the layer that produces it.
+
+
+The four-repo Nix split each owns a slice of the install:
+
+- [`nix-darwin`](/nix/nix-darwin) — system layer (macOS, Homebrew, security)
+- [`nix-home`](/nix/nix-home) — user dev environment (shell, git, linters, credentials)
+- [`nix-ai`](/nix/nix-ai) — AI tool ecosystem (Claude Code binary, Gemini CLI, MCP servers, MLX modules)
+- [`nix-devenv`](/nix/nix-devenv) — reusable per-language project shells
+- **`nix-claude-code` (this page)** — declarative Claude Code *configuration* on top of the binary that `nix-ai` packages
+
+`nix-ai` answers "where does the Claude Code binary come from?"; `nix-claude-code` answers "which plugins, hooks, MCP servers, and permissions should it have when it starts?".
+
+## Getting started
+
+
+
+ Add `nix-claude-code.url = "github:dryvist/nix-claude-code"` to your flake `inputs`. Set `nix-claude-code.inputs.nixpkgs.follows = "nixpkgs"` and the same for `home-manager` so versions stay aligned.
+
+
+ `nix flake init -t github:dryvist/nix-claude-code#minimal` (or `#flake-parts` if you already use flake-parts). The minimal template is the fastest way to see the wiring.
+
+
+ Set `programs.claude.enabledPlugins`, `programs.claude.mcpServers`, and `programs.claude.permissions` in your home-manager config. Run `home-manager switch` or `darwin-rebuild switch`.
+
+
+ `darwin-rebuild --rollback` (or the home-manager equivalent) reverts the entire Claude Code install — plugin set, permissions, hooks, MCP wiring — to the previous generation in one command.
+
+
+
+## Where to go next
+
+
+
+ The binary layer this configures.
+
+
+ Where the home-manager modules attach to your user env.
+
+
+ The marketplace this can install plugins from.
+
+
+ The cloud cron sibling that uses the same Claude Code install pattern.
+
+
+ Modules, templates, full README.
+
+
diff --git a/security/scrubbed-values.mdx b/security/scrubbed-values.mdx
index 41b0b56..92e0ab0 100644
--- a/security/scrubbed-values.mdx
+++ b/security/scrubbed-values.mdx
@@ -22,6 +22,14 @@ tier: 2
Never write a real value even in a "wrong" example — the example becomes committed text. Use `${USER}`, `${REPO_ROOT}`, `${MAINTAINER_EMAIL}`, `${NAS_HOST}`, `` for shape stand-ins where the placeholder needs to look like a variable.
+## Cost and subscription references
+
+The same posture applies to monetary costs and subscription tiers.
+
+- **Never name a specific AI subscription tier** (`Claude Max`, `ChatGPT Pro`, `GitHub Copilot Business`). Marketing tiers change names; the broadcast of personal-spend decisions has no business in public docs. Use the generic noun — `Claude subscription`, `ChatGPT subscription`, `Copilot subscription` — and describe the *mechanism* (`included monthly automation credits`, `metered API usage`) if it matters.
+- **Never state a monetary cost as `$N/day`.** The canonical unit on this site is monthly, with the per-instance figure and the instances-per-month count visible so the math is reviewable: `~$X per instance × ~Y instances/month ≈ ~$Z/month`. If you do not know the instance frequency, say `a small fixed monthly cost` instead of inventing one.
+- Schedule frequencies (`2×/day`, `weekly`) and rate limits (`5 attempts per PR per day`) are not costs and stay as-is.
+
## Portable path references
**Never commit absolute user paths** (`/Users/{username}/*`, `/home/{username}/*`, `$HOME/*`, `~/*`).
diff --git a/tools/automation.mdx b/tools/automation.mdx
index 9591952..3f28cbd 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.
+