From deedeecef98457d67dbfa07c3f90e3d1c77ab31d Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 18 Apr 2026 20:15:30 -0700 Subject: [PATCH] C0: adopt AGENTS.md as canonical agent config --- AGENTS.md | 165 ++++++++++++++++++ CLAUDE.md | 164 +---------------- README.md | 6 +- .../+agent-file-neutralization.ai.md | 1 + docs/tutorials/ai-assistance-guide.md | 4 +- docs/tutorials/exploring-the-docs.md | 8 +- mise-tasks/mikado-branch-invariant-check | 2 +- 7 files changed, 179 insertions(+), 171 deletions(-) create mode 100644 AGENTS.md create mode 100644 docs/changelog.d/+agent-file-neutralization.ai.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..80f9852 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,165 @@ +# AGENTS.md + +Guidance for AI agents working in this repository. See also [[ai-assistance-guide]]. + +## Overview + +blumeops is Erich Blume's GitOps repository for personal infrastructure, orchestrated via tailnet `tail8d86e.ts.net`. + +**CRITICAL: Public repo at github.com/eblume/blumeops - never commit secrets!** + +**Shell:** The user's interactive shell may differ from the current harness shell. Prefer repo-safe, non-interactive commands when possible, and match the user's shell conventions when giving interactive examples. + +## Rules + +1. **Always run `mise run ai-docs` at session start** + This will refresh your context with important information you will be assumed to know and follow. + **Read the full output** — never truncate, pipe to `head`/`tail`, or skip sections. + For problems with a large surface area, ask the user if `mise run ai-sources` should also be run — it concatenates all non-doc source files (~270K tokens) for deep codebase context. +2. **Always use `--context=minikube-indri` with kubectl** (or `--context=k3s-ringtail` for ringtail services) - work contexts must never be touched + **NEVER run `minikube delete`** — it destroys all PVs, etcd, and cluster state. Use `minikube stop`/`minikube start` for restarts. If minikube is stuck, see [[restart-indri]]. Full rebuild from scratch requires the DR procedure in [[rebuild-minikube-cluster]]. +3. **Classify the change as C0/C1/C2 before starting** (see below) — this determines branching and PR requirements +4. **Feature branches + PRs for C1/C2** - checkout main, pull, create branch, open PR via `tea pr create`. C0 goes direct to main. +5. **Check PR comments with `mise run pr-comments `** before proceeding +6. **Add changelog fragments (all change levels)** - `docs/changelog.d/..md` + Types: `feature`, `bugfix`, `infra`, `doc`, `ai`, `misc` + Applies to C0, C1, and C2 whenever the change is user-visible or noteworthy. + - **C1/C2:** Use branch name: `..md` + - **C0:** Use orphan prefix: `+..md` (avoids `main.*` collisions) +7. **Test before applying** - dry runs (`--check --diff`), syntax checks, `ssh indri '...'` +8. **Wait for user review before deploying** (C1/C2) +9. **Never merge PRs or push to main without explicit request** (C0 commits to main are fine) +10. **Verify deployments** - `mise run services-check` + +## Change Classification + +Before starting work, classify the change: + +| Class | Name | When to use | Key trait | +|-------|------|-------------|-----------| +| **C0** | Quick Fix | Small, low-risk, fix-forward safe | Direct to main, no PR | +| **C1** | Human Review | Moderate complexity or risk | Feature branch + PR, docs-first | +| **C2** | Mikado Chain | Multi-phase, multi-session, high complexity | Mikado Branch Invariant | + +**C0** — commit directly to main. No branch or PR needed. Fix forward if problems arise. + +**C1** — feature branch with early PR. Search related docs first, write documentation changes before code, deploy from the unmerged branch (ArgoCD `--revision`, Ansible from checkout). Upgrade to C2 if complexity spirals. + +**C2** — branch `mikado/` governed by the Mikado Branch Invariant: all card commits first, then code progress, then card closures. Commits use `C2(): plan/impl/close/finalize` convention. Reset the branch when new prerequisites are discovered. Resume with `mise run docs-mikado --resume`. + +See [[agent-change-process]] for the full methodology. + +## Project Structure + +``` +./docs/ # documentation (Diataxis, Quartz) +./docs/changelog.d/ # towncrier fragments +./.dagger/ # dagger pipelines +./.forgejo/ # forgejo-runner actions and workflows +./mise-tasks/ # scripts via `mise run` +./ansible/playbooks/ # ansible (indri.yml primary) +./ansible/roles/ # indri service roles +./argocd/apps/ # ArgoCD Application definitions +./argocd/manifests/ # k8s manifests per service +./fly/ # fly.io proxy for public routing +./pulumi/ # Pulumi IaC (tailnet ACLs, dns, cloud) +~/.config/{nvim,fish} # user's shell config, managed by chezmoi +~/code/personal/ # user's projects +~/code/personal/zk # user's Obsidian-sync managed zettelkasten. Potential source for reference data. +~/code/3rd/ # mirrored external projects +~/code/work # FORBIDDEN +``` +Other code paths will be listed via ai-docs, this is just an overview. When you +encounter wiki-links (`[[like-this]]`) it is referring to docs/ cards. + +## Service Deployment + +### Kubernetes (ArgoCD) + +Most services run in minikube on indri via ArgoCD (app-of-apps, manual sync). GPU workloads (Frigate, ntfy) run on ringtail's k3s cluster, also managed by ArgoCD. + +**PR workflow:** +1. Create branch, modify `argocd/manifests//` +2. Push. Sync 'apps' app if service definition changed (set --revision to branch). +3. Test on branch: `argocd app set --revision && argocd app sync ` +4. After merge: `argocd app set --revision main && argocd app sync ` + +**Commands:** `argocd app list|get|diff|sync ` + +**Login:** `argocd login argocd.ops.eblu.me --username admin --password "$(op read 'op://vg6xf6vvfmoh5hqjjhlhbeoaie/srogeebssulhtb6tnqd7ls6qey/password')"` + +### Indri (Ansible) + +Native services: Forgejo, Zot, Caddy, Borgmatic, Alloy + +```fish +mise run provision-indri # full +mise run provision-indri -- --tags # specific +mise run provision-indri -- --check --diff # dry run +``` + +### Routing + +| Domain | Mechanism | Reachable from | +|--------|-----------|----------------| +| `*.eblu.me` | Fly.io proxy (Tailscale tunnel) | public internet | +| `*.ops.eblu.me` | Caddy on indri | k8s pods, containers, tailnet | +| `*.tail8d86e.ts.net` | Tailscale MagicDNS | tailnet clients only | + +Check tailscale serve: `ssh indri 'tailscale serve status --json'` + +## Container Releases + +```fish +mise run container-list # show images/tags +mise run container-release # tag and build +``` +The goal is to eventually use only locally built containers in all cases, with +full supply chain control via forge.ops.eblu.me repositories, mirroring source +from upstream. + +**After triggering a build** (manual dispatch or push to main), verify the +workflow succeeded before proceeding: + +```fish +mise run runner-logs # find the run number +mise run runner-logs # see jobs in the run +mise run runner-logs -j # fetch logs on failure +``` + +This also works for other forge repos (`--repo eblume/hermes`). + +## Third-Party Projects + +Ask user to mirror on forge first, then clone to `~/code/3rd//`. + +### Sporked Projects + +Some mirrored projects are "sporked" — a floating-branch soft-fork strategy +where local patches are continuously rebased on top of upstream. See +[[spork-strategy]] and [[create-a-spork]] for the full methodology. + +Sporked projects live in `~/code/3rd//` with three remotes: +`origin` (eblume/ fork on forge), `mirror` (mirrors/ on forge), `upstream` +(canonical). The `blumeops` branch is the default; `deploy` merges everything. + +Create a new spork: `mise run spork-create ` + +## Task Discovery + +```fish +mise run blumeops-tasks # fetch from Todoist, sorted by priority +``` +Most tasks are stored in `./mise-tasks/`. For scripts with any logic or +complexity, use uv run --script 's with explicit dependencies. Complex +workflows with artifacts should become dagger pipelines. Mise tasks are for +development processes and operations - tools for the user or the agent. + +## Credentials + +Root store is 1Password. Never grab directly - use existing patterns (ansible +pre_tasks, external-secrets, scripts with `op` CLI). It's ok to use `op item +get` without `--reveal` to explore what secrets are available, however. + +Prefer `op read "op://vault/item/field"` over `op item get --fields` to avoid +quoting issues with multi-line values. diff --git a/CLAUDE.md b/CLAUDE.md index ee071cb..d825c0f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,165 +1,7 @@ # CLAUDE.md -Guidance for Claude Code working in this repository. See also [[ai-assistance-guide]]. +Claude Code compatibility shim. -## Overview +The canonical agent instructions for this repository now live in [`AGENTS.md`](AGENTS.md). -blumeops is Erich Blume's GitOps repository for personal infrastructure, orchestrated via tailnet `tail8d86e.ts.net`. - -**CRITICAL: Public repo at github.com/eblume/blumeops - never commit secrets!** - -**Shell:** The user's shell is **fish**. Use `$status` not `$?` for exit codes. Use fish syntax in interactive examples. - -## Rules - -1. **Always run `mise run ai-docs` at session start** - This will refresh your context with important information you will be assumed to know and follow. - **Read the full output** — never truncate, pipe to `head`/`tail`, or skip sections. - For problems with a large surface area, ask the user if `mise run ai-sources` should also be run — it concatenates all non-doc source files (~270K tokens) for deep codebase context. -2. **Always use `--context=minikube-indri` with kubectl** (or `--context=k3s-ringtail` for ringtail services) - work contexts must never be touched - **NEVER run `minikube delete`** — it destroys all PVs, etcd, and cluster state. Use `minikube stop`/`minikube start` for restarts. If minikube is stuck, see [[restart-indri]]. Full rebuild from scratch requires the DR procedure in [[rebuild-minikube-cluster]]. -3. **Classify the change as C0/C1/C2 before starting** (see below) — this determines branching and PR requirements -4. **Feature branches + PRs for C1/C2** - checkout main, pull, create branch, open PR via `tea pr create`. C0 goes direct to main. -5. **Check PR comments with `mise run pr-comments `** before proceeding -6. **Add changelog fragments (all change levels)** - `docs/changelog.d/..md` - Types: `feature`, `bugfix`, `infra`, `doc`, `ai`, `misc` - Applies to C0, C1, and C2 whenever the change is user-visible or noteworthy. - - **C1/C2:** Use branch name: `..md` - - **C0:** Use orphan prefix: `+..md` (avoids `main.*` collisions) -7. **Test before applying** - dry runs (`--check --diff`), syntax checks, `ssh indri '...'` -8. **Wait for user review before deploying** (C1/C2) -9. **Never merge PRs or push to main without explicit request** (C0 commits to main are fine) -10. **Verify deployments** - `mise run services-check` - -## Change Classification - -Before starting work, classify the change: - -| Class | Name | When to use | Key trait | -|-------|------|-------------|-----------| -| **C0** | Quick Fix | Small, low-risk, fix-forward safe | Direct to main, no PR | -| **C1** | Human Review | Moderate complexity or risk | Feature branch + PR, docs-first | -| **C2** | Mikado Chain | Multi-phase, multi-session, high complexity | Mikado Branch Invariant | - -**C0** — commit directly to main. No branch or PR needed. Fix forward if problems arise. - -**C1** — feature branch with early PR. Search related docs first, write documentation changes before code, deploy from the unmerged branch (ArgoCD `--revision`, Ansible from checkout). Upgrade to C2 if complexity spirals. - -**C2** — branch `mikado/` governed by the Mikado Branch Invariant: all card commits first, then code progress, then card closures. Commits use `C2(): plan/impl/close/finalize` convention. Reset the branch when new prerequisites are discovered. Resume with `mise run docs-mikado --resume`. - -See [[agent-change-process]] for the full methodology. - -## Project Structure - -``` -./docs/ # documentation (Diataxis, Quartz) -./docs/changelog.d/ # towncrier fragments -./.dagger/ # dagger pipelines -./.forgejo/ # forgejo-runner actions and workflows -./mise-tasks/ # scripts via `mise run` -./ansible/playbooks/ # ansible (indri.yml primary) -./ansible/roles/ # indri service roles -./argocd/apps/ # ArgoCD Application definitions -./argocd/manifests/ # k8s manifests per service -./fly/ # fly.io proxy for public routing -./pulumi/ # Pulumi IaC (tailnet ACLs, dns, cloud) -~/.config/{nvim,fish} # user's shell config, managed by chezmoi -~/code/personal/ # user's projects -~/code/personal/zk # user's Obsidian-sync managed zettelkasten. Potential source for reference data. -~/code/3rd/ # mirrored external projects -~/code/work # FORBIDDEN -``` -Other code paths will be listed via ai-docs, this is just an overview. When you -encounter wiki-links (`[[like-this]]`) it is referring to docs/ cards. - -## Service Deployment - -### Kubernetes (ArgoCD) - -Most services run in minikube on indri via ArgoCD (app-of-apps, manual sync). GPU workloads (Frigate, ntfy) run on ringtail's k3s cluster, also managed by ArgoCD. - -**PR workflow:** -1. Create branch, modify `argocd/manifests//` -2. Push. Sync 'apps' app if service definition changed (set --revision to branch). -3. Test on branch: `argocd app set --revision && argocd app sync ` -4. After merge: `argocd app set --revision main && argocd app sync ` - -**Commands:** `argocd app list|get|diff|sync ` - -**Login:** `argocd login argocd.ops.eblu.me --username admin --password "$(op read 'op://vg6xf6vvfmoh5hqjjhlhbeoaie/srogeebssulhtb6tnqd7ls6qey/password')"` - -### Indri (Ansible) - -Native services: Forgejo, Zot, Caddy, Borgmatic, Alloy - -```fish -mise run provision-indri # full -mise run provision-indri -- --tags # specific -mise run provision-indri -- --check --diff # dry run -``` - -### Routing - -| Domain | Mechanism | Reachable from | -|--------|-----------|----------------| -| `*.eblu.me` | Fly.io proxy (Tailscale tunnel) | public internet | -| `*.ops.eblu.me` | Caddy on indri | k8s pods, containers, tailnet | -| `*.tail8d86e.ts.net` | Tailscale MagicDNS | tailnet clients only | - -Check tailscale serve: `ssh indri 'tailscale serve status --json'` - -## Container Releases - -```fish -mise run container-list # show images/tags -mise run container-release # tag and build -``` -The goal is to eventually use only locally built containers in all cases, with -full supply chain control via forge.ops.eblu.me repositories, mirroring source -from upstream. - -**After triggering a build** (manual dispatch or push to main), verify the -workflow succeeded before proceeding: - -```fish -mise run runner-logs # find the run number -mise run runner-logs # see jobs in the run -mise run runner-logs -j # fetch logs on failure -``` - -This also works for other forge repos (`--repo eblume/hermes`). - -## Third-Party Projects - -Ask user to mirror on forge first, then clone to `~/code/3rd//`. - -### Sporked Projects - -Some mirrored projects are "sporked" — a floating-branch soft-fork strategy -where local patches are continuously rebased on top of upstream. See -[[spork-strategy]] and [[create-a-spork]] for the full methodology. - -Sporked projects live in `~/code/3rd//` with three remotes: -`origin` (eblume/ fork on forge), `mirror` (mirrors/ on forge), `upstream` -(canonical). The `blumeops` branch is the default; `deploy` merges everything. - -Create a new spork: `mise run spork-create ` - -## Task Discovery - -```fish -mise run blumeops-tasks # fetch from Todoist, sorted by priority -``` -Most tasks are stored in `./mise-tasks/`. For scripts with any logic or -complexity, use uv run --script 's with explicit dependencies. Complex -workflows with artifacts should become dagger pipelines. Mise tasks are for -development processes and operations - tools for the user or the agent. - -## Credentials - -Root store is 1Password. Never grab directly - use existing patterns (ansible -pre_tasks, external-secrets, scripts with `op` CLI). It's ok to use `op item -get` without `--reveal` to explore what secrets are available, however. - -Prefer `op read "op://vault/item/field"` over `op item get --fields` to avoid -quoting issues with multi-line values. +If a tool specifically looks for `CLAUDE.md`, read `AGENTS.md` and follow that file as the source of truth. diff --git a/README.md b/README.md index 8ba6b8d..e5945e5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Tools and configuration for Erich Blume's personal infrastructure, orchestrated across a Tailscale tailnet. This is a homelab, but it's also a testing ground for AI-assisted -infrastructure development. Much of this codebase was co-authored with [Claude +infrastructure development. Much of this codebase was initially co-authored with [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview), and the repo places heavy emphasis on documentation, process, and change classification to make that collaboration work well. I don't know entirely how @@ -77,7 +77,7 @@ mise run container-list # list tracked container images ## AI-assisted development This repo is designed to be worked on by both humans and AI agents. The -[`CLAUDE.md`](CLAUDE.md) file provides instructions for Claude Code, and the +[`AGENTS.md`](AGENTS.md) file provides shared instructions for agentic tools, and the [`docs/tutorials/ai-assistance-guide.md`](docs/tutorials/ai-assistance-guide.md) explains the full workflow. @@ -87,7 +87,7 @@ Changes are classified before starting work: - **C1** - feature branch + PR, documentation written before code - **C2** - multi-phase work using the Mikado method for dependency tracking -See the [agent change process](docs/how-to/agent-change-process.md) for +See the [agent change process](docs/explanation/agent-change-process.md) for details. ## License diff --git a/docs/changelog.d/+agent-file-neutralization.ai.md b/docs/changelog.d/+agent-file-neutralization.ai.md new file mode 100644 index 0000000..da16fba --- /dev/null +++ b/docs/changelog.d/+agent-file-neutralization.ai.md @@ -0,0 +1 @@ +Adopt `AGENTS.md` as the canonical agent instruction file, keep `CLAUDE.md` as a compatibility shim, and update docs to reference the neutral file and the correct agent-change-process path. diff --git a/docs/tutorials/ai-assistance-guide.md b/docs/tutorials/ai-assistance-guide.md index 9138526..3ee1ffa 100644 --- a/docs/tutorials/ai-assistance-guide.md +++ b/docs/tutorials/ai-assistance-guide.md @@ -10,7 +10,7 @@ tags: > **Audiences:** AI, Owner -This guide provides context for AI agents (like Claude Code) assisting with BlumeOps operations, and helps Erich understand how to work effectively with AI assistance. +This guide provides context for AI agents assisting with BlumeOps operations, and helps Erich understand how to work effectively with AI assistance. ## Critical Rules @@ -22,7 +22,7 @@ These are non-negotiable for AI agents working in this repo: 4. **Wait for user review before deploying** - Create PRs, don't auto-deploy 5. **Never merge PRs without explicit request** - The user merges after review -Full rules are in the repo's `CLAUDE.md`. See [[agent-change-process]] for the C0/C1/C2 change classification methodology — C0 (direct to main), C1 (feature branch + PR), C2 (Mikado Branch Invariant). +Full rules are in the repo's `AGENTS.md`. See [[agent-change-process]] for the C0/C1/C2 change classification methodology — C0 (direct to main), C1 (feature branch + PR), C2 (Mikado Branch Invariant). ## Workflow Conventions diff --git a/docs/tutorials/exploring-the-docs.md b/docs/tutorials/exploring-the-docs.md index 83aec43..2fd5f66 100644 --- a/docs/tutorials/exploring-the-docs.md +++ b/docs/tutorials/exploring-the-docs.md @@ -30,15 +30,15 @@ The docs follow the [Diataxis](https://diataxis.fr/) framework: You probably want quick access to operational details: - [How-to](/how-to/) guides for common operations (deploy, troubleshoot, update ACLs) - [Reference](/reference/) has service URLs, commands, and config locations -- [[ai-assistance-guide]] explains how to work effectively with Claude +- [[ai-assistance-guide]] explains how to work effectively with AI agents - Run `mise run ai-docs` to prime AI context with key documentation -### For Claude/AI Agents +### For AI Agents Context for effective assistance: - Read [[ai-assistance-guide]] for operational conventions - [Reference](/reference/) has the technical specifics you'll need -- The repo's `CLAUDE.md` has critical rules (especially the kubectl context requirement) +- The repo's `AGENTS.md` has critical rules (especially the kubectl context requirement) ### For External Readers @@ -81,7 +81,7 @@ The `ai-docs` mise task concatenates key documentation files for AI context: mise run ai-docs ``` -This outputs key documentation files and a full tree listing of all docs, providing Claude with essential context for BlumeOps operations. +This outputs key documentation files and a full tree listing of all docs, providing an agent with essential context for BlumeOps operations. ## Related diff --git a/mise-tasks/mikado-branch-invariant-check b/mise-tasks/mikado-branch-invariant-check index 8760a39..ca9f79a 100755 --- a/mise-tasks/mikado-branch-invariant-check +++ b/mise-tasks/mikado-branch-invariant-check @@ -294,7 +294,7 @@ def main( console.print(f" [red]✗[/red] {error}") console.print() console.print( - "[dim]See: docs/how-to/agent-change-process.md " + "[dim]See: docs/explanation/agent-change-process.md " "§ The Mikado Branch Invariant[/dim]" ) raise SystemExit(1)