From 31cb338dee76ad6b2cac7885ace413de7377fae6 Mon Sep 17 00:00:00 2001 From: stuffbucket <231133237+stuffbucket@users.noreply.github.com> Date: Wed, 15 Jul 2026 00:47:52 -0700 Subject: [PATCH] feat: add ops-* skill family for managing work in a GitHub repo Adds ops-workflow (semantic index/dispatcher), ops-investigate, ops-issue-decomposition, ops-github-hygiene, and ops-automation. Encodes an investigate -> decompose -> hygiene -> automate method with fixed child-issue/epic templates, a gh identity guard, and deterministic issue-only automation guidance. Regenerates README.md and llms.txt. --- README.md | 5 + llms.txt | 10 + .../skills/ops-automation/SKILL.md | 179 ++++++++++++++++++ .../references/drift-watcher-example.md | 118 ++++++++++++ .../skills/ops-github-hygiene/SKILL.md | 105 ++++++++++ .../scripts/gh-identity-guard.sh | 58 ++++++ .../skills/ops-investigate/SKILL.md | 114 +++++++++++ .../references/facet-checklist.md | 47 +++++ .../references/provenance-template.md | 37 ++++ .../skills/ops-issue-decomposition/SKILL.md | 161 ++++++++++++++++ .../assets/child-issue-template.md | 53 ++++++ .../assets/epic-template.md | 46 +++++ .../stuffbucket/skills/ops-workflow/SKILL.md | 91 +++++++++ 13 files changed, 1024 insertions(+) create mode 100644 plugins/stuffbucket/skills/ops-automation/SKILL.md create mode 100644 plugins/stuffbucket/skills/ops-automation/references/drift-watcher-example.md create mode 100644 plugins/stuffbucket/skills/ops-github-hygiene/SKILL.md create mode 100644 plugins/stuffbucket/skills/ops-github-hygiene/scripts/gh-identity-guard.sh create mode 100644 plugins/stuffbucket/skills/ops-investigate/SKILL.md create mode 100644 plugins/stuffbucket/skills/ops-investigate/references/facet-checklist.md create mode 100644 plugins/stuffbucket/skills/ops-investigate/references/provenance-template.md create mode 100644 plugins/stuffbucket/skills/ops-issue-decomposition/SKILL.md create mode 100644 plugins/stuffbucket/skills/ops-issue-decomposition/assets/child-issue-template.md create mode 100644 plugins/stuffbucket/skills/ops-issue-decomposition/assets/epic-template.md create mode 100644 plugins/stuffbucket/skills/ops-workflow/SKILL.md diff --git a/README.md b/README.md index 30ab09d..aa0c77f 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide, or read the [Best Pra | `ghostty-config` | Configure and optimize Ghostty terminal for any machine | | `git-workflow-skill` | Git operations and workflows | | `npm-trusted-publishing` | Publish npm packages from GitHub Actions using OIDC trusted publishing with provenance | +| `ops-automation` | Build deterministic, no-LLM, no-interactive-auth ops automation for maintaining a GitHub repo, plus the git hygiene for running many agents against one repo | +| `ops-github-hygiene` | Enforce org GitHub conventions before any outward GitHub action — identity guarding, label routing, Conventional-Commit titling, and commit-message hygiene | +| `ops-investigate` | Read-only investigation discipline that precedes writing issues or code — step 1 of the ops-workflow method | +| `ops-issue-decomposition` | Turn a body of investigation findings into a small set of focused child issues unified by one tracking epic, with explicit dependency and conflict sequencing | +| `ops-workflow` | Umbrella index and dispatcher for the ops-* skill family that manages a body of work in a GitHub repo | | `pages` | Root index for the pages-* family — a five-step pipeline that deploys a Vite project to GitHub Pages via GitHub Actions, including GitHub Enterprise (GHE) codespace-style subdomain discovery | | `pages-build-vite` | Locally builds the Vite project and verifies the dist/ output before committing | | `pages-commit-vite` | Stages and commits the GitHub Pages configuration files to the local git repo | diff --git a/llms.txt b/llms.txt index 141b252..b4412e0 100644 --- a/llms.txt +++ b/llms.txt @@ -63,6 +63,11 @@ Skill validation: ✓ ghostty-config ✓ git-workflow-skill ✓ npm-trusted-publishing + ✓ ops-automation + ✓ ops-github-hygiene + ✓ ops-investigate + ✓ ops-issue-decomposition + ✓ ops-workflow ✓ pages ✓ pages-build-vite ✓ pages-commit-vite @@ -191,6 +196,11 @@ Schema validation: All 3 file(s) valid. | ghostty-config | "Configure and optimize Ghostty terminal for any machine. Use when setting up... | — | | git-workflow-skill | Git operations and workflows. Use when committing, branching, merging, rebasi... | read_file, write_file, list_directory | | npm-trusted-publishing | "Publish npm packages from GitHub Actions using OIDC trusted publishing with ... | — | +| ops-automation | "Build deterministic, no-LLM, no-interactive-auth ops automation for maintain... | read_file, write_file, list_directory | +| ops-github-hygiene | Enforce org GitHub conventions before any outward GitHub action — identity gu... | — | +| ops-investigate | "Read-only investigation discipline that precedes writing issues or code — st... | — | +| ops-issue-decomposition | Turn a body of investigation findings into a small set of focused child issue... | — | +| ops-workflow | "Umbrella index and dispatcher for the ops-* skill family that manages a body... | — | | pages | Root index for the pages-* family — a five-step pipeline that deploys a Vite ... | — | | pages-build-vite | Locally builds the Vite project and verifies the dist/ output before committi... | — | | pages-commit-vite | Stages and commits the GitHub Pages configuration files to the local git repo... | — | diff --git a/plugins/stuffbucket/skills/ops-automation/SKILL.md b/plugins/stuffbucket/skills/ops-automation/SKILL.md new file mode 100644 index 0000000..cd53c0a --- /dev/null +++ b/plugins/stuffbucket/skills/ops-automation/SKILL.md @@ -0,0 +1,179 @@ +--- +name: ops-automation +description: "Build deterministic, no-LLM, no-interactive-auth ops automation for maintaining a GitHub repo, plus the git hygiene for running many agents against one repo. Use when standing up a drift watcher or scheduled maintenance job that reads a pinned source-of-truth constant and files one idempotent labelled issue instead of opening a PR; when separating ops tooling from product code under scripts/ops with its own test and typecheck lane; when deciding what an unattended CI job may poll (only public upstream authorities, never product secrets); or when isolating parallel-agent work in git worktrees while avoiding destructive stash or reset operations in a shared tree. Step 4 of the ops-workflow method." +license: MIT +allowed-tools: read_file write_file list_directory +metadata: + category: ops + domain: github-workflow +--- + +# Ops Automation + +Build unattended tooling that keeps a GitHub repo honest without a human or an +LLM in the loop, and run parallel agents against that repo without stepping on +each other. This is step 4 of the `ops-workflow` method: reach for it once the +manual workflow is stable and worth automating. + +This skill teaches the pattern. For generic git mechanics (branch, commit, +rebase, PR) load `git-workflow-skill`. For the label and identity conventions +of the issues this automation files, load `ops-github-hygiene`. Do not restate +their content here. + +For a full worked example distilled from a production drift watcher, read +[references/drift-watcher-example.md](references/drift-watcher-example.md). + +## The five principles + +Every ops automation you build must hold all five. They are not style +preferences — each one closes a specific failure mode. + +### 1. Deterministic, no LLM, no interactive auth + +The job must produce the same result every run given the same inputs. No model +call, no prompt, no browser login, no device-code flow. If a step cannot run +unattended in CI, it does not belong in the automation. + +The pinned value in source is the **single source of truth**. The tool *reads* +it; it never keeps a second copy to compare against. Duplicating the pin means +the two copies drift and the tool reports a value that no longer exists in the +product. + +```text +# WRONG — the tool now holds a stale duplicate of the real pin +const WATCHED_VERSION = "1.4.2" // must be kept in sync by hand, and won't be + +# RIGHT — read the real constant out of the product source at run time +const source = await readFile("src/config.ts", "utf8") +const current = extractPin(/const VERSION = "([\d.]+)"/, source) +``` + +Back the read with a **parity guard** test: a colocated unit test that runs the +same extractor against the real source file. If someone renames the pinned +constant, the extractor stops matching and the test goes red in tooling CI — +*before* the automation can ship a bogus value. The extractor throwing on a +non-match (rather than returning empty) is what makes the guard bite. + +### 2. Separate ops tooling from the product + +Ops automation maintains the product; it is not shipped in it. Keep it in a +dedicated directory (`scripts/ops/` or equivalent) with its **own** test and +typecheck lane, distinct from the product's. + +- Give it its own scripts, e.g. `check:ops` / `test:ops`, and its own test + root so the product's `test` command never picks up tooling tests. +- Run the ops lane only on PRs that touch that path (a `paths:` filter on the + tooling CI workflow). The product CI never runs tooling tests, and the + tooling CI never runs product tests. +- Document the tooling in an ops/admin doc, not in the product architecture + doc. It is operational surface, not product surface. + +This keeps a flaky upstream fetch or a tooling refactor from reddening product +CI, and keeps product churn from triggering tooling runs. + +### 3. Issue-only output, never auto-PR + +When the automation detects something actionable, prefer **filing or refreshing +one idempotent labelled issue** over opening a pull request. + +- One stable label (e.g. `external-drift`) identifies the issue. A run that + finds drift creates it if absent or refreshes its body if present — never a + second copy. A clean run **closes** the stale issue. +- Scope the body so a reconciliation PR can be derived from it directly: the + exact file to change, the current value, the target value, an + upstream-review link, and the acceptance step. +- Hand that scoped issue to the repo's triage / merge bot, which owns deriving + and landing the PR. The automation detects and reports; it does not rewrite + source. + +Why issue-only beats auto-PR: + +- Filing an issue needs only a plain `GITHUB_TOKEN` with `issues: write` — no + GitHub App token, no bot identity to provision. +- It sidesteps the "a bot-authored PR does not trigger CI" fragility that an + auto-PR path has to work around. +- It stays off require-PR rulesets entirely. +- A value is often duplicated in a coupled string (a version echoed inside a + User-Agent, say), so a mechanical single-pin rewrite would half-update the + source. A human-reviewed reconciliation guided by the issue body is the + correct, reviewable gate. + +Note: an issue created with the default `GITHUB_TOKEN` fires **no** +`issues.opened` event, so an event-driven triage workflow will not see it. Make +sure a poll-based triage backstop exists to pick it up, or drift goes silently +unhandled. + +### 4. No product secrets — watch only public authorities + +An unattended CI job must only poll **publicly verifiable** upstream +authorities: published releases, git blob SHAs, public catalogs. Anything you +can fetch without a product credential. + +An endpoint that sits behind an authed product token with no public mirror is +**not** polled from CI. Putting a product credential into CI to reach it is a +posture violation — call it out and stop. Instead: + +- Find a public proxy signal. If a schema only changes when a client version + changes, watch the client's public releases and let that stand in for the + schema. +- Where no public signal exists, document a **local fallback**: the maintainer + diffs the authed endpoint locally with their own credentials when a related + public watch fires. State this in the ops doc; do not automate it in CI. + +### 5. Git and parallel-agent hygiene + +Many agents against one repo will corrupt each other's work unless isolated. + +- **Isolate parallel work in a worktree.** Give each concurrent agent its own + checkout with `git worktree add ../work- `, and tear it down + with `git worktree remove` when done. Separate working trees cannot collide. + +```text +git worktree add ../ops-fix-abc feature/ops-fix-abc +# ... agent works in ../ops-fix-abc ... +git worktree remove ../ops-fix-abc +``` + +- **Never `git stash pop` or `git stash apply` in a shared working tree.** A + pop silently merges another worker's stash into your tree, and on conflict it + leaves a mess that invites destructive cleanup — real work has been lost this + way. If you must inspect a stash, do it **read-only**: + +```text +git stash show -p stash@{0} # inspect only — applies nothing +``` + +- **Never run history- or tree-destroying commands** that discard uncommitted + work: `git reset --hard`, `git checkout -- `, `git clean -fd`. To move + a branch use `git switch`, `git merge --ff-only`, or `git reset --keep`; to + unstage use `git reset --soft` or `git reset --mixed`. +- Always `git status` before any ref or history operation. If the tree is dirty + with changes you did not make, stop and surface it rather than clearing it. + +## Build checklist + +Before you ship an ops automation, confirm each item: + +- [ ] Runs unattended: no LLM call, no interactive auth, deterministic output. +- [ ] Reads the pinned source of truth; keeps no duplicate of it. +- [ ] A parity-guard test reds tooling CI if the pinned constant is renamed. +- [ ] Lives under `scripts/ops/` (or equivalent) with its own test/typecheck + lane, gated by a `paths:` filter; product CI and tooling CI stay disjoint. +- [ ] Files/refreshes one idempotent labelled issue; a clean run closes it. +- [ ] Never opens a PR; needs only `GITHUB_TOKEN` with `issues: write`. +- [ ] A poll-based triage backstop catches token-authored issues. +- [ ] Polls only public authorities; product-secret endpoints get a documented + local fallback, never a CI credential. +- [ ] Parallel work is isolated in worktrees; no `stash pop`/`apply`, no + `reset --hard` / `checkout -- ` / `clean -fd` in a shared tree. +- [ ] A failed check is reported as actionable drift, not swallowed or crashed. + +## Guidelines + +- Detect and report; do not mutate product source from the automation. The + scoped issue is the deliverable. +- Treat a failed check as drift, not a crash: surface it so a broken watcher + cannot sit silently green. Exit 0 from the script and let the workflow decide. +- This is one step of a larger method. Load `ops-workflow` to see where it + fits, `ops-github-hygiene` for the issue's label and identity conventions, + and `git-workflow-skill` for the underlying git mechanics. diff --git a/plugins/stuffbucket/skills/ops-automation/references/drift-watcher-example.md b/plugins/stuffbucket/skills/ops-automation/references/drift-watcher-example.md new file mode 100644 index 0000000..200d9b2 --- /dev/null +++ b/plugins/stuffbucket/skills/ops-automation/references/drift-watcher-example.md @@ -0,0 +1,118 @@ +# Worked example: an external-surface drift watcher + +A distilled walkthrough of a real, deterministic drift watcher, showing how the +five principles land in one tool. The subject repo impersonates several +third-party clients and mirrors an external API's wire contract. Those +upstreams live outside the repo and move on their own schedule, so a stale +hardcoded pin fails silently in production. The watcher turns "did an upstream +we mirror move?" into a deterministic, unattended check. + +This is a pattern, not code to copy. Adapt the shapes to your repo. + +## What it watches + +Each row pairs a pin that is the single source of truth in the product with the +public upstream authority it must track. + +| Pin (source of truth) | Upstream authority | Signal | +| --- | --- | --- | +| A client version constant in product source | That client's latest public release tag | Impersonated client version | +| A second client version, same file | Its own upstream release | Impersonated client version | +| A committed baseline blob SHA | An upstream spec-generated file's git blob SHA | Wire-contract change | +| Pinned API-version header date strings | A last-reviewed baseline value | Outbound header drift | + +The tool reads each pin out of the real source file at run time. It never keeps +a second copy of the version — only the *baseline* values that have no +machine-readable "latest" (a blob SHA, a reviewed header date) are stored, and +those are the value being tracked, not a duplicate of a live constant. + +## Principle 1 in practice: read, do not duplicate + +The extractor is a pure function over the source text. It **throws** on a +non-match, which is what arms the parity guard. + +```text +function extractPin(spec, source) { + const m = source.match(spec.pattern) // pattern captures the value in group 1 + if (!m?.[1]) { + throw new Error( + `pin ${spec.id}: pattern did not match ${spec.file} — was the constant renamed?`, + ) + } + return m[1] +} +``` + +The colocated test runs this same extractor against the real product file. If +someone renames the constant, the pattern stops matching, `extractPin` throws, +and the tooling CI goes red before the watcher can report a value that no +longer exists. + +## Principle 4 in practice: only public authorities + +- Version pins compare against the upstream project's latest **public release + tag** — a plain GitHub API fetch, no product credential. +- The wire-contract signal watches a spec-generated file's **git blob SHA**. A + SHA change stands in for a spec change; still fully public. +- One runtime endpoint returns schema **values** only behind an authed product + token with no public mirror. The watcher does **not** call it. Instead it + leans on a public proxy: that schema only changes when the client version + changes, which the release watch already catches. The documented fallback is + to diff the live schema locally with the maintainer's own credentials when + that watch fires. No product secret ever enters CI. + +## Principle 3 in practice: issue-only reconciliation + +Detection is separated from reconciliation: + +- The script computes drift and, when anything drifted, writes a **Markdown + issue body** scoped for direct PR derivation: the exact file to change, the + current value, the target value, an upstream-review link, and the reconcile + step. +- The daily workflow files or refreshes **one** issue under a stable label + (e.g. `external-drift`). A clean run closes the stale issue. The workflow + never opens a PR — it needs only `GITHUB_TOKEN` with `issues: write`. +- A failed check is emitted as drift (with the error in the note), never + swallowed, so a broken watcher cannot sit silently green. + +Because the issue is created with the default token, it fires no +`issues.opened` event; a **poll-based triage backstop** picks it up so it is +never missed. Deriving and landing the reconciliation PR is the triage/merge +bot's job — or a maintainer's, from the labelled issue. + +Why not auto-PR the bump: a version value is often duplicated in a coupled +string (e.g. echoed inside a User-Agent), so a mechanical single-pin rewrite +would half-update the source. Reconciling by hand, guided by the issue body, is +the correct reviewable gate. It also keeps the job off require-PR rulesets and +avoids bot-PR-CI fragility. + +## Principle 2 in practice: separation of concerns + +- **Code** lives under `scripts/ops/`, pure and deterministic, exporting its + pure functions so the network-free logic is unit-testable. +- **Test** is colocated (`scripts/ops/*.test.ts`) and kept out of the product + test root. It is the parity guard. +- **Tooling CI** runs the colocated test only on PRs that touch + `scripts/ops/**`. The daily watcher also self-checks before it acts. Product + CI never runs tooling tests. +- **Docs** live in the ops/admin area, not the product architecture doc. + +## Reconciling a flag + +Whoever picks up the labelled issue: + +- **Version pin** — review the linked upstream release for behavioural changes, + then bump the pin. Reconcile **every** occurrence, since the bare value may + also appear in a coupled string. +- **Spec/blob SHA** — review the upstream file's history for new or changed + fields, reconcile any affected product types, then bump the committed + baseline SHA in the same change. +- **Header date pin** — read the provider changelog for breaking changes, then + bump the last-reviewed baseline value in the same change. + +## Running locally + +```text +run watch:drift # detect; writes an issue-body report if anything drifted +run test:ops # the parity-guard tooling test, in its own lane +``` diff --git a/plugins/stuffbucket/skills/ops-github-hygiene/SKILL.md b/plugins/stuffbucket/skills/ops-github-hygiene/SKILL.md new file mode 100644 index 0000000..8510ba8 --- /dev/null +++ b/plugins/stuffbucket/skills/ops-github-hygiene/SKILL.md @@ -0,0 +1,105 @@ +--- +name: ops-github-hygiene +description: Enforce org GitHub conventions before any outward GitHub action — identity guarding, label routing, Conventional-Commit titling, and commit-message hygiene. Use before creating or editing issues, PRs, comments, labels, or commits in an org repo; when running any ops script that calls gh; when deciding which label (needs-bot vs needs-triage) an issue should carry; when writing an issue or PR title; or when a gh action may be running under the wrong logged-in account. Step 3 of the ops-workflow method. +license: MIT +metadata: + category: ops + domain: github-workflow +--- + +# Ops GitHub Hygiene + +Apply these org rules BEFORE any outward GitHub action (creating or editing +issues, PRs, comments, labels, or pushing commits). This is step 3 of the +`ops-workflow` method. + +For generic git mechanics — branching, rebase, conflict resolution, PR +creation flow — use the `git-workflow-skill`. This skill adds only the +org-specific identity, label, routing, titling, and message deltas. + +## 1. Identity guard (do this first) + +Many machines have several accounts logged into `gh`, and the active one is +often the wrong one. Acting under the wrong account leaks an unrelated +identity onto public artifacts. Confirm the active account matches the org's +intended identity, and switch if not, before any outward `gh` action. + +Run the guard at the top of any ops script: + +```bash +bash /scripts/gh-identity-guard.sh +``` + +It checks `gh api user -q '.login'`, runs `gh auth switch --user ` +when the active account is wrong, and aborts non-zero if the expected account +is not logged in — so downstream `gh` calls never run as the wrong identity. + +Keep the git commit author consistent with the confirmed `gh` identity, so +commits and GitHub actions attribute to the same person: + +```bash +git config user.name "Expected Name" +git config user.email "expected@users.noreply.github.com" +``` + +## 2. Labels and routing + +Use the org's actual labels: `bug`, `enhancement`, `documentation`. + +Routing labels decide who handles the issue: + +- `needs-bot` routes an issue to AUTONOMOUS handling by the repo's + triage/merge bot. Add it ONLY to actionable, self-contained child issues + that the bot can pick up and complete on its own. +- `needs-triage` is for everything else and is usually applied + automatically. Pure tracking or epic issues get `needs-triage`. + +Critical rule: NEVER add `needs-bot` to a tracking or epic issue. The bot +would try to autonomously act on work that is not self-contained. Epics +coordinate child issues; only the children may carry `needs-bot`. + +## 3. Titling + +Issue and PR titles are Conventional-Commit-prefixed and area-scoped. The +scope names the touched area: + +```text +fix(models): correct token count for streamed responses +feat(ops): add gh identity guard to release script +refactor(config): collapse duplicate token-store loaders +docs(release): clarify squash-merge subject rule +test(ws): cover presence-registry reconnect path +epic(config): unify config sources +``` + +Squash-merge uses the PR TITLE as the commit subject, so the title must +itself be one valid Conventional Commit. Only `feat:` and `fix:` cut a +release — pick the prefix accordingly. + +Add cross-references in titles and bodies to link related work: + +```text +fix(models): guard null usage block (follow-up to #312) +test(ws): reproduce reconnect drop (surfaced by #298) +``` + +## 4. Commit-message hygiene + +Org rule for all git messages: + +- No assistant or tool self-references. +- No emojis. + +Keep the subject a valid Conventional Commit and let the body explain the +why, not the how. + +## Checklist + +Before any outward GitHub action, confirm: + +- Identity guard passed for the org's expected login. +- Git author matches the confirmed `gh` identity. +- Labels use real org labels; `needs-bot` only on self-contained children. +- Title is a single valid Conventional Commit, area-scoped, with the right + release-cutting prefix and any cross-references. +- Commit messages carry no self-references and no emojis. diff --git a/plugins/stuffbucket/skills/ops-github-hygiene/scripts/gh-identity-guard.sh b/plugins/stuffbucket/skills/ops-github-hygiene/scripts/gh-identity-guard.sh new file mode 100644 index 0000000..0273a4e --- /dev/null +++ b/plugins/stuffbucket/skills/ops-github-hygiene/scripts/gh-identity-guard.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# gh-identity-guard.sh — confirm the active `gh` account matches an expected +# org login before any outward GitHub action, switching if possible. +# +# Many machines have several accounts logged into `gh`; the active one is often +# wrong. Acting under the wrong account leaks an unrelated identity onto public +# artifacts (issues, PRs, comments). Run this as a guard at the top of any ops +# script that performs outward `gh` actions. +# +# Usage: +# bash gh-identity-guard.sh +# +# Exit codes: +# 0 active account matches (or was switched to) +# 1 usage error, gh missing/unauthenticated, or unable to reach expected +set -euo pipefail + +expected="${1:-}" + +if [[ -z "$expected" ]]; then + echo "ERROR: expected login required." >&2 + echo "Usage: bash gh-identity-guard.sh " >&2 + exit 1 +fi + +if ! command -v gh >/dev/null 2>&1; then + echo "ERROR: gh CLI not found on PATH. Install from https://cli.github.com" >&2 + exit 1 +fi + +# Query the currently active account. Fails if no account is authenticated. +if ! current="$(gh api user -q '.login' 2>/dev/null)"; then + echo "ERROR: no authenticated gh account. Run: gh auth login" >&2 + exit 1 +fi + +if [[ "$current" == "$expected" ]]; then + echo "OK: active gh account is '$expected'." + exit 0 +fi + +echo "WARN: active gh account is '$current', expected '$expected'. Switching..." >&2 + +if ! gh auth switch --user "$expected" >/dev/null 2>&1; then + echo "ERROR: could not switch to '$expected'. It is not logged in to gh." >&2 + echo " Run: gh auth login --user $expected (then re-run this guard)" >&2 + echo "ABORT: refusing to act as '$current' in place of '$expected'." >&2 + exit 1 +fi + +# Re-confirm after switching — never trust the switch blindly. +if ! confirmed="$(gh api user -q '.login' 2>/dev/null)" || [[ "$confirmed" != "$expected" ]]; then + echo "ERROR: switch did not land on '$expected' (now '${confirmed:-unknown}')." >&2 + exit 1 +fi + +echo "OK: switched active gh account to '$expected'." +exit 0 diff --git a/plugins/stuffbucket/skills/ops-investigate/SKILL.md b/plugins/stuffbucket/skills/ops-investigate/SKILL.md new file mode 100644 index 0000000..448f646 --- /dev/null +++ b/plugins/stuffbucket/skills/ops-investigate/SKILL.md @@ -0,0 +1,114 @@ +--- +name: ops-investigate +description: "Read-only investigation discipline that precedes writing issues or code — step 1 of the ops-workflow method. Scout a problem to map its facets, then fan out parallel read-only subagents (one per facet) and produce a facet-indexed findings map where every claim cites file:path:line. Then verify each load-bearing claim against an authoritative external source and record provenance. Use when you must understand an unfamiliar system, gather facts, or confirm a claim before changing anything; when preparing to decompose work into issues; or when a surprising value needs tracing to its origin. Never author blind. Feeds ops-issue-decomposition." +license: MIT +metadata: + category: ops + domain: github-workflow +--- + +# Ops: Investigate Before Acting + +Do the read-only research that must happen before you write an issue or a line +of code. This skill covers step 1 of the `ops-workflow` method: +INVESTIGATE-BEFORE-ACTING and VERIFY-AGAINST-OTHERS. Its output is a +facet-indexed findings map plus a provenance block, which feed directly into +`ops-issue-decomposition`. + +Investigation is read-only. Make no edits until the findings map and provenance +block exist and every load-bearing claim is verified. + +## Scout, then fan out + +Never author blind. Map the problem before you dig into it. + +### Scout first + +Spend a short pass building a rough map of the problem's facets: which code +paths, docs, config, and history are plausibly involved. The scout is cheap and +disposable — its only job is to name the facets worth a dedicated deep read. + +### Fan out, one subagent per facet + +Launch read-only subagents in parallel, one per facet. Launch the whole fan-out +in a single batch so the agents run concurrently — do not spawn them one at a +time and wait. Typical facets to sweep: + +- Code and dispatch paths — where the behavior actually lives. +- Docs and decision records (ADRs) — stated intent and prior rationale. +- Configuration — pins, flags, environment, defaults. +- Git history, branches, and PRs — how and when the fact entered. + +For the complete facet sweep and what each subagent must return, read +`references/facet-checklist.md`. + +Keep every subagent read-only. They gather and cite; they do not edit. + +## Cite file:path:line in every finding + +The deliverable is a findings map indexed by facet, not prose. Each entry is a +claim with an exact citation: + +```text +FACET: +CLAIM: the specific fact found +CITATION: file/path:line (absolute path, exact line) +NOTE: any ambiguity or follow-up needed +``` + +Rules: + +- Every claim carries a `file:path:line` citation. A claim with no citation is + a guess — drop it or mark it explicitly unverified. +- Index by facet, not by narrative. The map is a lookup table, not an essay. +- Stay read-only through this step. Findings describe the current state; they + do not change it. + +## Verify each load-bearing claim + +A load-bearing claim is one a later decision or issue will rest on. Verify each +against an AUTHORITATIVE EXTERNAL source — not just the repo that stated it. + +Sources in priority order: + +1. Official API or product documentation. +2. The upstream project's SOURCE repository. +3. The EXACT PR or commit that introduced the fact. + +Trace surprising values to their origin. A price, limit, or default copied from +another provider's entry is NOT first-party — say so explicitly. In-repo +agreement is not verification: two files can be wrong together. + +Distinguish first-party and authoritative facts from inferred or copied ones. +Label every claim with how you know it, never assume. + +## Produce a provenance block + +Record provenance for each verified claim, labeling its authority level. This +block feeds the "Verified against / learning from others" section that +`ops-issue-decomposition` consumes. + +Use the template in `references/provenance-template.md`. Each entry names the +claim, its source, the authority level (first-party / upstream-source / +introducing-commit / inferred / copied), and a link or ref that a reader can +open to check for themselves. + +## Cross-references — do not duplicate + +- This is step 1 of `ops-workflow`. Load that index through the skill-router + MCP `get_skill` tool if you need the full method map. +- The findings map and provenance block are the input to + `ops-issue-decomposition`. Load it next: `get_skill("ops-issue-decomposition")`. +- For generic git inspection mechanics — log, blame, diff, branch and PR + archaeology — use `git-workflow-skill`. Do not restate git commands here. + +## Guidelines + +- Never author blind. If no findings map exists yet, you are not ready to write + an issue or code. +- Scout, then fan out in one batch. Serial subagent launches waste the + concurrency this step depends on. +- No claim without a `file:path:line` citation. +- No load-bearing claim without external verification and recorded provenance. +- Investigation is read-only. The first edit belongs to a later step, after the + findings and provenance exist. diff --git a/plugins/stuffbucket/skills/ops-investigate/references/facet-checklist.md b/plugins/stuffbucket/skills/ops-investigate/references/facet-checklist.md new file mode 100644 index 0000000..bf43c3c --- /dev/null +++ b/plugins/stuffbucket/skills/ops-investigate/references/facet-checklist.md @@ -0,0 +1,47 @@ +# Facet Checklist + +The facets to sweep during the fan-out. Assign one read-only subagent per +facet you judge relevant. Skip a facet only when the scout showed it cannot +touch the problem. Each subagent returns findings in the map format from +`SKILL.md` — claim plus `file:path:line`. + +## Code and dispatch paths + +- Where the behavior is defined, and where it is invoked from. +- The dispatch or routing layer that selects this path over alternatives. +- Call sites, entry points, and the boundaries the value crosses. +- Tests that exercise the path — they document expected behavior. + +Return: the defining symbol, its file/path:line, and each caller. + +## Docs and decision records (ADRs) + +- ADRs or decision records that mention the system or value. +- Specs, PRDs, and design notes stating intent. +- README or inline doc comments that explain rationale. + +Return: the stated intent, and whether it matches the code found above. + +## Configuration + +- Pins, version constraints, and lockfile entries. +- Feature flags, environment variables, and their defaults. +- Config files that set the value under investigation. + +Return: the effective value, its source file/path:line, and any override chain. + +## Git history, branches, and PRs + +- The commit that introduced or last changed the fact (use blame). +- The PR that merged it and its review discussion. +- Open branches or PRs that would change it again. + +Return: the introducing commit or PR ref, its date, and the stated reason. +Use `git-workflow-skill` for the underlying git mechanics. + +## Sweep discipline + +- Keep every subagent read-only. Gather and cite; do not edit. +- Launch the whole set in one batch for concurrency. +- A facet with no findings is still a result — record "no evidence found" so + the gap is visible to the next step. diff --git a/plugins/stuffbucket/skills/ops-investigate/references/provenance-template.md b/plugins/stuffbucket/skills/ops-investigate/references/provenance-template.md new file mode 100644 index 0000000..0c12b4e --- /dev/null +++ b/plugins/stuffbucket/skills/ops-investigate/references/provenance-template.md @@ -0,0 +1,37 @@ +# Provenance Template + +Fill one entry for each load-bearing claim. The completed block feeds the +"Verified against / learning from others" section of `ops-issue-decomposition`. + +## Authority levels + +Label each source with exactly one level, most authoritative first: + +- `first-party` — official API or product documentation for the thing itself. +- `upstream-source` — the upstream project's source repository. +- `introducing-commit` — the exact PR or commit that introduced the fact. +- `inferred` — deduced from indirect evidence; not directly stated anywhere. +- `copied` — taken from another provider's or system's entry; NOT first-party + for this subject. Name what it was copied from. + +## Entry format + +```text +CLAIM: the load-bearing fact +IN-REPO CITATION: file/path:line where the repo asserts it +SOURCE: authoritative external reference (URL, upstream path, or commit/PR ref) +AUTHORITY: first-party | upstream-source | introducing-commit | inferred | copied +VERIFIED: yes | no +NOTE: mismatch, staleness, or the origin a copied value traces back to +``` + +## Rules + +- One authority level per entry. If two apply, use the more authoritative and + note the other. +- In-repo agreement is not verification. A repo citing itself stays `inferred` + until an external source confirms it. +- Trace surprising values to origin. If a value was copied from elsewhere, the + authority is `copied` and the `NOTE` must name the source it came from. +- An unverified load-bearing claim blocks decomposition — mark `VERIFIED: no` + and surface it rather than hiding it. diff --git a/plugins/stuffbucket/skills/ops-issue-decomposition/SKILL.md b/plugins/stuffbucket/skills/ops-issue-decomposition/SKILL.md new file mode 100644 index 0000000..45c9cd9 --- /dev/null +++ b/plugins/stuffbucket/skills/ops-issue-decomposition/SKILL.md @@ -0,0 +1,161 @@ +--- +name: ops-issue-decomposition +description: Turn a body of investigation findings into a small set of focused child issues unified by one tracking epic, with explicit dependency and conflict sequencing. This is step 2 of the ops-workflow method. Use after a triage or research pass produces findings you must convert into trackable work — when asked to "break this into issues", "file child issues under an epic", "decompose these findings", "split this into workstreams", plan a merge order, or detect issues that will conflict because they edit the same files. Covers grouping findings into workstreams, the fixed child-issue and epic body templates, and computing a safe landing order across siblings. +license: MIT +metadata: + category: ops + domain: github-workflow +--- + +# Ops: Issue Decomposition + +Convert a pile of findings into a small set of **workstreams**, file one +**child issue** per workstream under a single **tracking epic**, and pin down +the **order** they must land in. Findings that stay in a research log never get +fixed; findings that become one giant issue never get reviewed. This skill is +the bridge: it makes the work small, attributable, and safely sequenceable. + +Inputs you should already have: a set of findings, each with a symptom, a +mechanism (root cause), and ideally a file:line location. If you only have +symptoms, go back and finish triage first — a child issue without a mechanism +is a bug report, not a workstream. + +## 1. Decompose findings into workstreams + +Group findings by **change surface**, not by symptom. Two findings belong in +the same workstream when fixing them touches the same module, the same layer, +or the same subsystem — because they will be reviewed, tested, and merged as a +unit. + +- Cluster by area: which directory / module / boundary the fix lives in. +- Keep workstreams small enough that one reviewer can hold the whole change in + their head. If a workstream spans three unrelated subsystems, split it. +- Aim for a handful of workstreams, not one per finding and not one for + everything. Merge trivially-related findings; split anything that mixes + concerns. + +Then map one-to-one: **one workstream → one child issue**, and **all child +issues → one epic**. Do not create an epic per workstream; the epic is the +single roof over the whole effort. + +## 2. Write each child issue + +Every child issue uses the same fixed body template so reviewers always know +where to look. The full template is +`/assets/child-issue-template.md` — copy it verbatim and fill each +section. The sections are: + +- `## Problem / root cause` — the symptom **and** the mechanism that produces + it. Not just "X is broken"; say why X breaks. +- `## Fix` — the concrete change, with **exact `file:line` references**. A + reviewer should be able to open the file at that line and see the spot. +- `## Verified against / learning from others` — authoritative external + sources with links (upstream docs, specs, the code you are matching). Label + each source **first-party** (the authoritative source itself) vs + **inferred/copied** (a blog, an answer, a sibling project you are imitating). +- `## Dependency / ordering` — what must land before or after this, which + siblings conflict because they touch the same file/lines, or the single word + `independent`. +- `## Scope` — which workstream this is, its sibling issues, and a link to the + epic. + +Do not invent extra sections and do not drop sections. The fixed shape is what +makes a set of child issues reviewable as a batch. + +## 3. Write the epic + +One epic tracks the whole effort. The full template is +`/assets/epic-template.md`. Its sections are: + +- `## Why` — the problem space and what the investigation surfaced. +- `## Target architecture / understanding` — the end-state you are steering + toward, so each child can be judged against it. +- `## Tracked pieces` — a checklist that links **every** child issue, one line + each: `- [ ] #NNN — one-liner`. +- `## Provenance of this understanding` — the sources behind the plan and their + authority level (first-party vs inferred), so a later reader can tell what is + load-bearing fact vs working assumption. + +The epic is the durable record. If someone reads only the epic, they should +understand the problem, the target, every piece of work, and how much to trust +the reasoning. + +## 4. Sequence the workstreams + +Before you publish, compute the landing order. Two issues that edit the **same +file or the same lines** will conflict on merge — that is the primary signal. + +1. List every child issue against the files/lines it changes. +2. Flag any pair that overlaps on a file (and especially on adjacent lines) — + these cannot land in parallel. +3. For each conflict or logical prerequisite, state a direction: **"B before + C"**, **"D after C"**. A fix that another fix builds on lands first. +4. Everything with no overlap and no prerequisite is **independent / + parallelizable** — say so explicitly; silence reads as "unknown". + +Record the resulting order in **two** places: + +- the epic's `## Tracked pieces` (order the checklist, or add a one-line + ordering note), and +- each child's `## Dependency / ordering` section (the per-issue view). + +Keeping both in sync is the point: the epic gives the merge captain the global +order, and each child tells its own author what to wait for. + +## Worked example (compact) + +A child issue: + +```markdown +## Problem / root cause +Origin guard rejects valid same-site requests: `isTrusted()` compares the raw +`Origin` header against `config.host` but never strips the port, so +`https://app.local:8443` never matches `app.local`. + +## Fix +Normalize both sides before compare in `src/lib/auth/origin-guard.ts:42` — +parse to URL and compare `.hostname`, not the raw string. + +## Verified against / learning from others +- WHATWG URL spec, "host vs hostname" (first-party). +- Matching normalization in `opencode`'s guard, `packages/auth/origin.ts` + (inferred/copied — we mirror their approach, not authoritative). + +## Dependency / ordering +Conflicts with #331 (also edits origin-guard.ts). Land this before #331. + +## Scope +Workstream: auth. Siblings: #331, #332. Epic: #338. +``` + +The epic that roofs it: + +```markdown +## Why +Triage of the single-window redesign surfaced five auth/routing defects that +block the new transport. Findings in research_log/2026-07-origin.md. + +## Target architecture / understanding +One origin guard, host-normalized, shared by HTTP and WS paths. + +## Tracked pieces +- [ ] #330 — normalize host in origin guard (land first) +- [ ] #331 — reuse guard on the WS upgrade path (after #330) +- [ ] #332 — presence registry cleanup (independent) + +## Provenance of this understanding +- WHATWG URL spec (first-party). +- opencode auth module (inferred — imitated, not authoritative). +``` + +This shape was proven on real issues `#330` (child) and `#338` (epic) in +`stuffbucket/maximal`. + +## Checklist before you publish + +- Every finding maps into exactly one workstream; no orphans. +- One child issue per workstream, all filled from the fixed template. +- Exactly one epic, its checklist links every child. +- Every same-file/same-line conflict is flagged and given a direction. +- Independent items are labeled `independent`, not left silent. +- The order appears in both the epic and each child's dependency section. diff --git a/plugins/stuffbucket/skills/ops-issue-decomposition/assets/child-issue-template.md b/plugins/stuffbucket/skills/ops-issue-decomposition/assets/child-issue-template.md new file mode 100644 index 0000000..7cab90a --- /dev/null +++ b/plugins/stuffbucket/skills/ops-issue-decomposition/assets/child-issue-template.md @@ -0,0 +1,53 @@ +# Child issue body template + +Copy everything below the line into the issue body. Keep all five headings, +in this order. Do not add or remove sections. + +--- + +## Problem / root cause + +State the **symptom** (what is observably wrong) and the **mechanism** (why it +happens). One without the other is incomplete: a symptom with no mechanism is a +bug report; a mechanism with no symptom is a hypothesis. + +- Symptom: what a user or test observes. +- Mechanism: the specific code path / condition that produces it. + +## Fix + +Describe the concrete change and point at **exact locations**. Use +`path/to/file.ext:LINE` (or `:START-END`) so a reviewer can jump straight to +the spot. + +- Change: what to add / remove / alter. +- Location(s): `src/…/file.ts:NN`, `src/…/other.ts:NN-MM`. + +## Verified against / learning from others + +List the authoritative sources you checked or copied from, each with a link and +an authority label: + +- `[Source name](url)` — **first-party** (the spec / upstream docs / the code + being matched — authoritative). +- `[Source name](url)` — **inferred/copied** (a blog, a Q&A answer, a sibling + project you are imitating — not authoritative; verify before trusting). + +If you had no external source, say so: "No external source; derived from +in-repo behavior at `file:line`." + +## Dependency / ordering + +State how this issue relates to its siblings: + +- Must land **after** #NNN (this builds on it). +- Must land **before** #NNN (that one builds on this). +- **Conflicts** with #NNN — both edit `path/to/file.ext` (same lines / adjacent + lines); they cannot land in parallel. Proposed order: this one first. +- Or: **independent** — no shared files, no prerequisite; parallelizable. + +## Scope + +- Workstream: the area/change-surface this belongs to. +- Siblings: #NNN, #NNN (the other children in this effort). +- Epic: #NNN. diff --git a/plugins/stuffbucket/skills/ops-issue-decomposition/assets/epic-template.md b/plugins/stuffbucket/skills/ops-issue-decomposition/assets/epic-template.md new file mode 100644 index 0000000..65032c5 --- /dev/null +++ b/plugins/stuffbucket/skills/ops-issue-decomposition/assets/epic-template.md @@ -0,0 +1,46 @@ +# Epic body template + +Copy everything below the line into the epic body. Keep all four headings, in +this order. The epic is the single roof over every child issue in this effort. + +--- + +## Why + +The problem space and what the investigation surfaced. Describe the shape of +the problem, what triggered the work, and link the source findings (research +log, triage notes, failing report). A reader who knows nothing should finish +this section understanding *why this effort exists*. + +## Target architecture / understanding + +The end-state you are steering toward. Describe the intended structure once, so +each child issue can be judged against it ("does this move us toward the +target?"). This is the shared mental model; keep it concrete but not +implementation-by-implementation. + +## Tracked pieces + +A checklist linking **every** child issue, one line each, in landing order. +Reflect the merge sequence here (annotate prerequisites inline): + +- [ ] #NNN — one-line summary (land first) +- [ ] #NNN — one-line summary (after #NNN) +- [ ] #NNN — one-line summary (independent) +- [ ] #NNN — one-line summary (before #NNN — shared file) + +Ordering rule: anything that another piece builds on, or that edits a file a +sibling also edits, comes first. Independent pieces can go in any order — mark +them so. + +## Provenance of this understanding + +The sources behind this plan and how much to trust each. A later reader must be +able to separate load-bearing fact from working assumption: + +- `[Source name](url)` — **first-party** (authoritative: spec, upstream docs, + the code being matched). +- `[Source name](url)` — **inferred/copied** (imitated from a sibling project + or third-party writeup; not authoritative). +- Assumptions still unverified: list them explicitly so they can be checked + later. diff --git a/plugins/stuffbucket/skills/ops-workflow/SKILL.md b/plugins/stuffbucket/skills/ops-workflow/SKILL.md new file mode 100644 index 0000000..feb0462 --- /dev/null +++ b/plugins/stuffbucket/skills/ops-workflow/SKILL.md @@ -0,0 +1,91 @@ +--- +name: ops-workflow +description: "Umbrella index and dispatcher for the ops-* skill family that manages a body of work in a GitHub repo. Acts as a semantic table of contents: routes you to the right ops-* sub-skill to load next via the skill-router. Use when planning, decomposing, or shipping a body of work in a GitHub repo; turning an investigation into child issues and a tracking epic; sequencing workstreams by dependency; setting gh identity, labels, needs-bot vs needs-triage routing, or Conventional-Commit titles; building deterministic no-LLM ops automation such as drift watchers or scheduled issue-only jobs; or whenever you are unsure which ops-* skill to use next and need to pick one. Does not itself perform the steps — it names the sibling skill to fetch and the order to run them." +license: MIT +metadata: + category: ops + domain: github-workflow +--- + +# Ops Workflow + +Index for the `ops-*` skill family. This skill is a map, not a manual: it +does not perform the work. It routes you to the one sibling skill that owns +the step you are about to take, and tells you the order the steps chain in. +Load the named sibling through the skill-router before acting. + +## The method + +Managing a body of work in a GitHub repo follows four steps. Each step has a +dedicated sibling skill that holds the actual procedure. Do not reproduce +that procedure here — load the sibling when you reach its step. + +1. Investigate before acting; verify findings against independent sources. +2. Decompose the work into workstreams: child issues plus one tracking epic, + sequenced by dependency. +3. Establish identity and GitHub hygiene before any outward action. +4. Build deterministic, no-LLM ops automation for ongoing drift and schedules. + +Generic git mechanics (the low-level branch, commit, and PR moves the steps +above depend on) are not a step of their own — they are a shared dependency; +reach for `git-workflow-skill` when you need them. + +## How to load the next skill + +Every sibling lives in this same repo and is fetched on demand through the +skill-router MCP, not read inline. Load one with: + +```text +get_skill("ops-investigate") +``` + +which maps to the tool `mcp__plugin_stuffbucket_skill-router__get_skill`. If +you do not yet know which sibling you need, search first: + +```text +list_skills("ops") +``` + +which maps to `mcp__plugin_stuffbucket_skill-router__list_skills`. Load at +most one or two skills per task — if you need more, the task is under- +decomposed, so go back to step 2. + +## Intent to next-skill routing + +Match your immediate intent to a row, load the named skill through the +skill-router, then follow that skill. Do not act from this table alone. + +| If you are about to… | Load this skill next | +| --- | --- | +| Understand an unfamiliar system, gather facts, or confirm a claim before changing anything; fan out read-only research and record provenance | `ops-investigate` | +| Turn findings into work: split into workstreams, open child issues under one tracking epic, write issue bodies, order them by dependency | `ops-issue-decomposition` | +| Take any outward GitHub action: check `gh` identity, apply labels, route `needs-bot` vs `needs-triage`, or write Conventional-Commit titles | `ops-github-hygiene` | +| Stand up recurring automation: drift watchers, scheduled jobs, deterministic no-LLM tooling that emits issues only, with worktree and git hygiene | `ops-automation` | +| Perform generic git mechanics — branching, committing, rebasing, resolving conflicts, opening a PR — that the steps above rely on | `git-workflow-skill` | + +## Recommended order and chaining + +Run the steps in order; each feeds the next. + +- Start with `ops-investigate`. Its verified findings are the input to + decomposition — do not decompose from an unverified premise. +- Feed those findings into `ops-issue-decomposition`. The child issues and + tracking epic it produces are the unit of work everything else references. +- Before you open, label, or comment on anything from step 2, pass through + `ops-github-hygiene` once to fix identity and labeling. It gates every + outward action, so revisit it whenever you touch GitHub. +- Reach for `ops-automation` only once the manual workflow is stable and + worth automating. It emits issues, which flow back into step 2. +- Drop into `git-workflow-skill` at any point you need the underlying git or + PR mechanics; it is a dependency of the other steps, not a phase of its own. + +## Guidelines + +- This skill only routes. If you find yourself doing the work here, stop and + load the sibling that owns it. +- Pick exactly one sibling per intent. When two rows seem to match, you are + probably mid-transition between steps — choose the earlier step. +- The family is: `ops-workflow` (this index), `ops-investigate`, + `ops-issue-decomposition`, `ops-github-hygiene`, `ops-automation`, plus the + generic `git-workflow-skill`. Cross-reference them by name; never restate + their content.