From d6c4e98031ad5bef76b83961b078d13be3aa63e1 Mon Sep 17 00:00:00 2001 From: Aditya Sheth Date: Tue, 16 Jun 2026 11:44:39 +0530 Subject: [PATCH] chore: sync maintainer loop skills --- .claude/skills/github-project-triage/SKILL.md | 81 ++++++++ .../skills/maintainer-orchestrator/SKILL.md | 174 ++++++++++++++++++ docs/MAINTAINER_LOOP.md | 164 +++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 .claude/skills/github-project-triage/SKILL.md create mode 100644 .claude/skills/maintainer-orchestrator/SKILL.md create mode 100644 docs/MAINTAINER_LOOP.md diff --git a/.claude/skills/github-project-triage/SKILL.md b/.claude/skills/github-project-triage/SKILL.md new file mode 100644 index 0000000..185623c --- /dev/null +++ b/.claude/skills/github-project-triage/SKILL.md @@ -0,0 +1,81 @@ +--- +name: github-project-triage +description: >- + Triage GitHub issues and PRs into maintainer-facing item cards and sort them + into autonomous / needs-owner / defer buckets. Use when asked to "triage" a + repo or the backlog, or as the first step of the maintainer-orchestrator loop. + Defaults to the current repo; only goes org-wide when explicitly told ("all", + "broad", "org-wide"). +--- + +# GitHub Project Triage + +Turn an open queue into decisions. Triage produces **item cards** a maintainer +can act on, then sorts them so the orchestrator knows what to do. + +## Scope + +- If invoked inside a Git repo with a GitHub remote (or a Routine cloned a + specific repo), triage **only that repo**. +- Only broaden to multiple repos / the whole `ITSEZMONEY` org when the prompt + explicitly says `all`, `broad`, `org-wide`, or `everything`. + +## Discover the queue + +Use the **GitHub MCP connector** (preferred) or `gh`: +- Open PRs (with CI status, requested reviewers, draft state, mergeability). +- Open issues (with labels, age, last activity). +- For each, the last few comments and the CI job summary. + +There is no `repobar` dependency — query GitHub directly. Batch reads where the +connector allows it; don't fetch full logs until an item needs them. + +## Item card + +For every open issue/PR, produce: + +| Field | What to capture | +| --- | --- | +| **URL / #** | Direct link. | +| **What** | One line: what this issue/PR is. | +| **Why it matters** | Impact / who it affects. | +| **Author trust** | Per the auto-merge allowlist: `adityash8` = **trusted**; everyone else — other org members, returning/first-time/external contributors, forks, and bots (Dependabot/Renovate) — = **untrusted**. State the factual signal, not a vibe. | +| **Type** | bug · feature · dependency · security · docs · chore | +| **Fit** | good / mixed / poor + one-line reason (does it match the product?). | +| **Risk** | low / medium / high + blast radius (what breaks if wrong). | +| **Proof** | CI state · local repro · test coverage · manual/E2E validation. | +| **Blockers** | Missing repro, failing CI, needs decision, needs creds, merge conflict. | +| **Next action** | The single concrete next step. | + +Trust here is an **explicit allowlist for auto-merge**, not a vibe: only +`adityash8` is trusted. Untrusted authorship doesn't mean a bad patch — it means +the item cannot auto-merge and routes to **needs-owner** (draft PR), even with +green CI. The sole exception is a **bot dependency bump**, which may auto-merge +if it meets every low-risk gate. Trust never substitutes for green CI and a sane +diff; cite factual signals (account, prior merged PRs) when noting it. + +## Risk heuristics (ITSEZMONEY / gate-slip context) + +Treat as **high risk** anything touching: auth/Passport/OAuth, payments/Stripe, +`certificates/` or any key/secret, DB schema or migrations (`shared/schema.ts`), +the parsing pipeline's output shape (`flightDataSchema`, the dual-AI parsers), +or CI/release/`.env`/infra/deployment config. Treat docs, copy, tests, +lockfile/dep bumps, and formatting as **low risk**. + +## Buckets + +Sort every card into exactly one: + +1. **Autonomous** — fixable without product input. Note a confidence level and + why it's safe. (The orchestrator's autonomy policy decides land-vs-draft.) +2. **Needs owner** — blocked on a decision, direction, missing credentials, or + high-risk surface. State the exact decision required and the options. +3. **Defer / close** — stale, duplicate, superseded, or out of scope. Give the + reason and the canonical item if it's a duplicate. + +## Output + +Group the cards by bucket, most actionable first. Keep each card to a few lines. +End with a one-line tally (`N autonomous · M needs-owner · K defer`). When run as +the orchestrator's first step, hand this structured result back rather than +printing a wall of text. diff --git a/.claude/skills/maintainer-orchestrator/SKILL.md b/.claude/skills/maintainer-orchestrator/SKILL.md new file mode 100644 index 0000000..6affb3f --- /dev/null +++ b/.claude/skills/maintainer-orchestrator/SKILL.md @@ -0,0 +1,174 @@ +--- +name: maintainer-orchestrator +description: >- + Control-plane loop for maintaining ITSEZMONEY repositories. Use when asked to + "maintain the repos", "run the maintainer loop", "triage and act on the + backlog", or when invoked by a scheduled/API/GitHub-event cloud Routine. It + inspects issues/PRs/CI across one or more repos, classifies each item as + autonomous / needs-owner / defer, lands low-risk work autonomously, escalates + the rest, and records state in a durable ledger issue. +--- + +# Maintainer Orchestrator + +A **control-plane** skill: inspect, classify, act, monitor, and report. It does +not invent product direction — it moves the existing backlog toward a clean, +green, reviewed state and escalates anything that needs a human decision. + +This skill is designed to run **autonomously inside a Claude Code Routine** +(`claude.ai/code/routines`) — a cloud session with no permission prompts — but +also works when invoked by hand in an interactive session. + +## The loop (one run) + +Each run is one pass. Do these in order, then stop. + +1. **Scope (owner-guarded).** Determine the target repositories. + - If the Routine cloned multiple repos, act on all of them; if invoked in a + single repo with a GitHub remote, act on that repo only. + - **Owner allowlist:** only operate on repos whose owner is `adityash8` or + `ITSEZMONEY`. Skip any cloned repo outside those owners entirely — no + triage, no PR, no merge — and record the skip in the **run summary**, never + in the skipped repo (you have no business writing to it). + - Do **not** broaden beyond the cloned/selected repos unless the prompt says + `all` / `org-wide`. +2. **Triage.** Invoke the `github-project-triage` skill to build item cards for + every open issue and PR (URL, what/why, author trust, fit, risk, proof/test + state, blockers, next action) and sort them into three buckets: + **autonomous · needs-owner · defer/close**. +3. **Read the ledger (single, race-safe).** Locate the repo's ledger by the + `maintainer-ledger` label (canonical), falling back to the exact title + `🤖 Maintainer Loop — Ledger`. **Exactly one must stay open.** If several are + open — two runs can race and each create one — keep the **lowest-numbered** + open ledger, close the rest as duplicates (`state_reason: duplicate` with + `duplicate_of` set to the survivor), and continue on the survivor. If none exists, create it, ensure the + `maintainer-ledger` label exists, and apply it. Never open a second ledger + when one is already open. The ledger is the durable memory across runs (cloud + sessions are ephemeral); skip anything it marks resolved or `wontfix`. +4. **Act on autonomous items**, one at a time, verifying each before moving on + (see *Autonomy policy* below). Stop touching an item the moment it stops + meeting the bar and reclassify it as needs-owner. +5. **Escalate needs-owner items.** Don't guess at product/direction/secret + decisions. Label `needs-owner`, leave a one-paragraph comment stating the + decision required and the options, and record it in the ledger. +6. **Defer/close** stale, duplicate, or superseded items with a short reason. +7. **Report.** Update the ledger issue with what changed this run (landed, + escalated, deferred, still-in-flight) and the timestamp. Keep it skimmable. + +Then end the run. The next trigger starts the next pass. + +## Autonomy policy + +The owner has authorized **auto-merge for low-risk work**. Be conservative: +when in doubt, downgrade to a draft PR and escalate. + +### Low-risk → may land autonomously (open PR, get CI green, then merge) + +ALL must hold: +- CI is **fully green** on the head commit (not pending, not skipped-as-proxy). +- The change is one of: dependency patch/minor bump that is non-breaking, + lockfile sync, docs/comments/typo, formatting or lint autofix, test-only + additions, or a clearly-scoped config tweak. +- Diff is small and bounded (rule of thumb: ≤ ~50 changed lines, single + concern). +- It touches **none** of: auth, payments/Stripe, `certificates/` or any + key/secret, DB schema or migrations (`shared/schema.ts`, drizzle), CI/release + config, infra/deployment config, or anything in `.env*`. +- Author is **trusted** per the allowlist below (or the change was authored by + this loop, which runs as `adityash8`) — **or** it is a bot dependency bump that + independently meets every other gate in this list. +- No unresolved review thread requests changes. +- Branch is up to date with base (or can be safely fast-forward updated). + +**Trusted author — explicit allowlist, not "CI is green":** +- An item counts as from a trusted author **only** if the PR author is + `adityash8`. No one else qualifies — other org members, first-time/external + contributors, forks, and bot accounts (including Dependabot/Renovate) are + **not** trusted. +- Bot **dependency** PRs may be auto-merged **only** when they also pass every + low-risk gate above; bot authorship alone never grants trust for anything else. + +For these: ensure CI is green, then **merge** (squash by default). If the loop +authored the fix, open the PR first, let CI run, then merge once green. + +### Everything else → draft PR + escalate, never auto-merge + +Bugs with real logic changes, features, anything touching the protected areas +above, large diffs, untrusted authors, red/uncertain CI, or any ambiguity: +open or leave a **draft** PR with your analysis, label `needs-owner`, and record +it in the ledger. Do not merge. + +### Never + +- Force-push to or merge into protected/long-lived branches outside the PR flow. +- Touch certificates, private keys, or secrets. +- Close an item a human is actively discussing. +- Make product/pricing/architecture decisions. + +## CI babysitting + +When you open or own a PR, drive its CI to green: read failing job logs, +reproduce locally when possible, push the fix, re-check. Re-kick on each failure +rather than treating one round as the job. If a failure is real and out of scope +or you've re-kicked several times without progress, escalate with the diagnosis +instead of looping forever. + +## Parallelization (worker fan-out) + +Within a single run you may fan out read-only investigation across repos/items +using the `Agent` tool (one subagent per repo or per cluster of related items). +Keep **mutations** (commits, merges, comments) on the main orchestrator thread so +there is a single writer. Workers investigate and report; the orchestrator +decides and acts. Workers must not spawn further workers. + +## Tools + +Use the **GitHub MCP connector** for all GitHub reads/writes (issues, PRs, CI +status, job logs, merges, comments, labels); fall back to `gh` only if the +connector is unavailable. Use `git` for local branch work. The `github-project-triage` +skill provides the queue-discovery and scoring helpers. + +Close duplicate ledgers via the connector/REST API so `state_reason: duplicate` +(+ `duplicate_of`) sticks — verified working. Note `gh issue close --reason` +accepts only `completed`/`not_planned`, so if you must use `gh`, close with +`not_planned` and a comment linking the survivor instead. + +## The ledger + +One ledger issue per repo, marked with the `maintainer-ledger` label (the +canonical identifier) and titled `🤖 Maintainer Loop — Ledger`. **Exactly one +stays open** — if a run finds duplicates it keeps the lowest-numbered open one, +closes the others as duplicates, and rewrites the survivor's body. Locate by +label first (titles can be edited), then by title. Body rewritten each run. +Suggested shape: + +``` +## Maintainer Loop — last run + +### Landed this run +- #123 bump zod 3.x → 3.y (deps, CI green) — merged + +### Needs owner +- #130 Stripe webhook retry policy — needs decision: idempotency window? (options A/B) + +### In flight +- #128 fix PDF parse crash — draft PR open, CI red, investigating + +### Deferred / closed +- #99 closed as duplicate of #128 +``` + +This is the only state that survives between runs — keep it accurate. + +> Seeing duplicate ledgers? That means two runs started concurrently — check the +> routine's triggers for overlap (e.g. a manual **Run now** firing alongside a +> scheduled run, or two schedule/event triggers). The dedup above heals it, but +> overlapping triggers also burn double the runs. + +## Reporting back to the owner + +A Routine run has no human present, so "escalation" means: the `needs-owner` +label, a concise comment on the item, and a ledger entry. Do not block on +`AskUserQuestion` (there is nobody to answer mid-run). If a connector is +configured (Slack/Linear/etc.), a one-line summary of escalations is welcome, +but the ledger is the source of truth. diff --git a/docs/MAINTAINER_LOOP.md b/docs/MAINTAINER_LOOP.md new file mode 100644 index 0000000..4a4bdfc --- /dev/null +++ b/docs/MAINTAINER_LOOP.md @@ -0,0 +1,164 @@ +# Maintainer Loop (cloud) + +A self-running maintenance loop for ITSEZMONEY repos: wake on a trigger → triage +issues/PRs/CI → land low-risk work autonomously → escalate the rest → record +state in a ledger issue. Built on two committed skills and **Claude Code +Routines** (Anthropic's cloud automation), so it keeps running with no laptop +open. + +- Playbook: [`.claude/skills/maintainer-orchestrator`](../.claude/skills/maintainer-orchestrator/SKILL.md) +- Triage: [`.claude/skills/github-project-triage`](../.claude/skills/github-project-triage/SKILL.md) + +## How it maps to "wake every 5 min, direct work to threads" + +| Your model | Cloud equivalent | +| --- | --- | +| Always-on driver | A **Routine** on Anthropic's cloud (no laptop). | +| Wake every N minutes | Routine **trigger** (schedule / GitHub event / API). | +| Triage + classify | `github-project-triage` skill. | +| Direct work to threads | Orchestrator fans out per-repo work with the `Agent` tool; mutations stay on one thread. | +| Some work lands autonomously | Routines run with **no permission prompts**; autonomy policy gates what merges. | +| Steer / report | The `🤖 Maintainer Loop — Ledger` issue (durable across runs). | + +## ⚠️ Cadence reality check + +Claude Code Routine **scheduled** triggers have a **one-hour minimum** — a true +"every 5 minutes" schedule is rejected. Pick a trigger based on how reactive you +need to be: + +| Want | Use | Notes | +| --- | --- | --- | +| Steady backlog sweeps | **Scheduled** trigger | Hourly is the floor; daily/weeknightly is usually plenty and cheaper. | +| Real-time on PRs/releases | **GitHub event** trigger | Fires on `pull_request.*` / `release.*` — the closest thing to "instant". Subject to per-account hourly caps. | +| Literal 5-minute polling | **API** trigger + external pinger | Your own cron/codex driver POSTs to the routine's `/fire` endpoint every 5 min. This is where a 5-min cadence actually lives. | + +You can attach **several triggers to one routine** — e.g. hourly sweep *plus* +react to every new PR *plus* an API endpoint your driver can poke. + +## Setup (once per loop) + +Routines can't be created from inside a web session or via API — create them in +the UI or with `/schedule` from a local CLI. + +1. **Commit the skills.** They must live in the repo(s) the routine clones — + that's what this PR does. The fan-out script (below) copies them to every + ITSEZMONEY repo. +2. **Create the routine** at → **New routine**: + - **Repositories:** select the repos to maintain. One routine can clone + several, so a single org-maintainer routine can sweep them all in one run. + - **Environment:** `Default` (Trusted network) is fine. Add `DATABASE_URL`, + `ANTHROPIC_API_KEY`, etc. only if a run needs to actually build/run the app. + - **Connectors:** keep **GitHub**; drop the rest unless a run needs them. + - **Permissions:** enable **Allow unrestricted branch pushes** only if you + want it to push to existing branches; PR-merge of low-risk items works + without it. + - **Prompt:** paste the kickoff prompt below. + - **Trigger:** pick from the cadence table above. +3. **Run now** once to seed the ledger issue and confirm behavior, then let the + trigger drive it. + +> Identity note: a routine acts as **you** — commits, PRs, and merges carry your +> GitHub user, and it draws down your account's usage + daily routine-run cap. + +### Kickoff prompt (paste into the routine) + +``` +Load the maintainer-orchestrator skill and run one maintenance pass over every +repository cloned for this run, following the skill's loop and autonomy policy +exactly: triage, read/update the "🤖 Maintainer Loop — Ledger" issue, act per +the autonomy rules below, then summarize the run in the ledger. + +SCOPE GUARD +- Only operate on repositories whose owner is `adityash8` or `ITSEZMONEY`. +- If a cloned repo is outside those owners, skip it entirely (no triage, no + merge, no PR) and log the skip in the run output (not the skipped repo's ledger). + +TRUSTED AUTHOR — explicit allowlist, not "CI is green" +- An item is from a "trusted author" ONLY if the PR author is one of: + `adityash8`. No one else qualifies — first-time/external contributors, + forks, and bot accounts (including dependabot, renovate) are NOT trusted. +- Dependency PRs from bots may be AUTO-MERGED only when they also pass every + low-risk gate below; bot authorship alone never grants trust for anything else. + +AUTONOMY +- AUTO-MERGE only when ALL hold: CI fully green; change is limited to + deps/docs/tests/lockfile/formatting; small diff; touches NONE of + auth/payments/certs/schema/CI/secrets/infra; and author is trusted (above) + OR it's a bot dependency bump meeting these same gates. +- Everything else: open a DRAFT PR and apply the `needs-owner` label. Do not merge. +- Defer or close stale or duplicate items. +- Never make product, pricing, or architecture decisions. When in doubt, + escalate (draft + needs-owner) instead of merging. +``` + +## Autonomy policy (summary) + +Authorized level: **auto-merge low-risk**. Full rules in the orchestrator skill. + +- **Scope guard:** only repos owned by `adityash8` or `ITSEZMONEY`; skip and log + anything else. +- **Trusted author = `adityash8` only.** Other org members, external/first-time + contributors, forks, and bots (Dependabot/Renovate) are **untrusted**. +- **Auto-merge** (squash) only when *all* hold: CI green · change is + deps/docs/tests/lockfile/formatting/safe-config · small bounded diff · touches + none of {auth, Stripe, `certificates/`, schema/migrations, CI/release, infra, + `.env*`, secrets} · author trusted (or loop-authored) **or** a bot dependency + bump meeting all these gates · no unresolved change requests. +- **Draft PR + `needs-owner`** for anything else — bugs with real logic, features, + protected surfaces, big diffs, untrusted authors, uncertain CI, any ambiguity. +- **Never** touch certs/keys/secrets, force-push protected branches, close an + item under active discussion, or make product/pricing/architecture decisions. + +## Fan out / sync to all repos + +The skill lives in each repo's `.claude/skills/`, so propagation = copy the +**current `gate-slip` versions** over the top of every target repo. The target +list is `scripts/maintainer-target-repos.txt` (the 27 other ITSEZMONEY repos + +the adityash8 repos; `gate-slip` is the source, not a target). + +> ⚠️ **Overwrite, don't skip.** Every target already contains +> `.claude/skills/maintainer-orchestrator/`, so any "skip if it exists" logic +> propagates **nothing** — that's how repos get stuck on an old skill version. +> Both paths below overwrite and only open a PR where content actually changed. + +### A. Distributor routine (cross-owner, no local `gh`) + +A one-off Routine with **every target repo *plus* `gate-slip` selected** (it +copies *from* the gate-slip checkout). Prompt: + +``` +gate-slip holds the canonical maintainer-loop files at +.claude/skills/maintainer-orchestrator/, .claude/skills/github-project-triage/, +and docs/MAINTAINER_LOOP.md. For every OTHER cloned repo, copy those three paths +from the gate-slip checkout OVER the top of whatever is there. If nothing changed +(already identical), skip that repo. Otherwise create/update branch +chore/maintainer-loop, commit "chore: sync maintainer loop skills", and open or +update a DRAFT PR titled "Sync maintainer loop skills". Do not modify gate-slip. +Report each repo as created / updated / unchanged, with the PR URL. +``` + +### B. Shell script (`gh`, cross-owner) + +```bash +scripts/fanout-maintainer-skills.sh # all repos in scripts/maintainer-target-repos.txt +scripts/fanout-maintainer-skills.sh ITSEZMONEY/serum adityash8/llmx # explicit owner/repo +``` + +It copies over the top and opens a PR only where content changed (idempotent). + +After either path, **add every repo to the maintainer routine's repo list** so +the loop actually runs against them. + +## Alternative: GitHub Actions (true 5-min, BYO key) + +If you'd rather not depend on Routines for sub-hour cadence, a scheduled Action +(`on: schedule: cron`, 5-min floor) can run the Claude Code GitHub Action with +the same kickoff prompt and an `ANTHROPIC_API_KEY` secret. It's more setup and +burns Action minutes + API spend per repo, so prefer Routines unless you need +the tighter cadence in CI. See . + +## References + +- Routines / scheduled tasks: +- Claude Code on the web: +- Skills: