commit d709f75edb4f36856f01984a1e4995ec4f19d0de Author: Hongming Wang Date: Wed May 6 13:53:38 2026 -0700 import from local vendored copy (2026-05-06) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8fb9d3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,5 @@ +name: CI +on: [push, pull_request] +jobs: + validate: + uses: Molecule-AI/molecule-ci/.github/workflows/validate-plugin.yml@main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2af45b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Credentials — never commit. Use .env.example as the template. +.env +.env.local +.env.*.local +.env.* +!.env.example +!.env.sample + +# Private keys + certs +*.pem +*.key +*.crt +*.p12 +*.pfx + +# Secret directories +.secrets/ + +# Workspace auth tokens +.auth-token +.auth_token diff --git a/.molecule-ci/scripts/requirements.txt b/.molecule-ci/scripts/requirements.txt new file mode 100644 index 0000000..3aecde9 --- /dev/null +++ b/.molecule-ci/scripts/requirements.txt @@ -0,0 +1 @@ +pyyaml>=6.0 diff --git a/.molecule-ci/scripts/validate-plugin.py b/.molecule-ci/scripts/validate-plugin.py new file mode 100644 index 0000000..c42e916 --- /dev/null +++ b/.molecule-ci/scripts/validate-plugin.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Validate a Molecule AI plugin repo.""" +import os, sys, yaml + +errors = [] + +# 1. plugin.yaml exists +if not os.path.isfile("plugin.yaml"): + print("::error::plugin.yaml not found at repo root") + sys.exit(1) + +with open("plugin.yaml") as f: + plugin = yaml.safe_load(f) + +# 2. Required fields +for field in ["name", "version", "description"]: + if not plugin.get(field): + errors.append(f"Missing required field: {field}") + +# 3. Version format +v = str(plugin.get("version", "")) +if v and not all(c in "0123456789." for c in v): + errors.append(f"Invalid version format: {v}") + +# 4. Runtimes type +runtimes = plugin.get("runtimes") +if runtimes is not None and not isinstance(runtimes, list): + errors.append(f"runtimes must be a list, got {type(runtimes).__name__}") + +# 5. Has content +content_paths = ["SKILL.md", "hooks", "skills", "rules"] +found = [p for p in content_paths if os.path.exists(p)] +if not found: + errors.append("Plugin must contain at least one of: SKILL.md, hooks/, skills/, rules/") + +# 6. SKILL.md formatting check +if os.path.isfile("SKILL.md"): + with open("SKILL.md") as f: + first_line = f.readline().strip() + if first_line and not first_line.startswith("#"): + print("::warning::SKILL.md should start with a markdown heading (e.g., # Plugin Name)") + +if errors: + for e in errors: + print(f"::error::{e}") + sys.exit(1) + +print(f"✓ plugin.yaml valid: {plugin['name']} v{plugin['version']}") +if found: + print(f" Content: {', '.join(found)}") +if runtimes: + print(f" Runtimes: {', '.join(runtimes)}") diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ff4324a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,112 @@ +# molecule-ai-plugin-molecule-workflow-triage + +Provides the `/triage` slash command — a full on-demand PR-triage cycle +comprising 7-gate verification, mechanical fixes, code review, cross-vendor +review, merge, docs sync, and issue pickup. Equivalent to one hourly cron tick, +triggered manually. + +**Version:** 1.0.0 +**Runtime:** `claude_code` +**Command:** `/triage` +**Skill:** `triage-workflow` + +--- + +## What It Does + +`/triage` runs the same workflow as the hourly cron tick, on demand: + +1. **Activate guards** — load `careful-mode` REFUSE/WARN/ALLOW lists; replay last 20 cron-learnings. +2. **List open PRs and issues** — via `gh pr list` and `gh issue list`. +3. **7-gate verification per PR:** + - Gate 1: CI status + - Gate 2: Build + - Gate 3: Tests + - Gate 4: Security + - Gate 5: Design review + - Gate 6: Line review + - Gate 7: Playwright (UI PRs only) + - Supplement A: `code-review` skill + - Supplement B: `cross-vendor-review` for noteworthy PRs (auth/billing/data-deletion/migration) +4. **Mechanical fixes on-branch** — fix Gate N failures with `fix(gate-N): ...` commits. Never fix logic/design/auth on-branch. +5. **Merge** — if all gates pass + 0 🔴 from code-review + cross-vendor agreement → merge (commit only, never squash). +6. **Docs sync** — invoke `update-docs` skill after any merge. +7. **Issue pickup (cap 2)** — gates I-1..I-6, branch + implement + draft PR + `llm-judge` ≥ 4. +8. **Status report + cron-learnings** — report + append 1–3 lines to cron-learnings JSONL. + +### When to use + +- Clear PR backlog faster than hourly cadence. +- Test changes to the triage prompt itself. +- Resume after a scheduled cron has died. + +--- + +## Repository Layout + +``` +molecule-workflow-triage/ +├── skills/ +│ └── triage-workflow/ +│ └── SKILL.md # Full triage workflow definition +├── commands/ +│ └── triage.md # /triage command definition +├── adapters/ +│ ├── __init__.py +│ └── claude_code.py # Claude Code harness adapter +├── plugin.yaml # Plugin manifest +└── README.md +``` + +--- + +## Key Conventions + +| Topic | Convention | +|---|---| +| **Merge type** | Merge commit only — never squash or rebase | +| **On-branch fixes** | Mechanical fixes only (`fix(gate-N): ...`) | +| **Merge requirement** | All 7 gates + 0 🔴 + cross-vendor agreement | +| **Issue cap** | Max 2 issues picked up per triage run | +| **llm-judge threshold** | Score ≥ 4 to mark draft PR ready | +| **Gates that block** | Security (Gate 4), Design (Gate 5), code-review 🔴 | + +--- + +## Standing Rules (Inviolable) + +- Never push to `main`. +- `careful-mode` REFUSE list **always blocks**. +- Code-review 🔴 **always blocks merge**. +- Cross-vendor disagreement on noteworthy PRs **escalates to user**. +- `llm-judge` ≤ 2 **blocks marking a draft PR ready**. +- Never fix logic/design/auth issues on-branch — file a separate issue. + +--- + +## Recommended Plugins + +`/triage` calls these skills as sub-steps. Install them first: + +```yaml +plugins: + - molecule-skill-code-review # Supplement A + - molecule-skill-cron-learnings # Step 5 learnings + - molecule-skill-llm-judge # Issue pickup scoring + - molecule-skill-update-docs # Docs sync after merge +``` + +--- + +## Development + +```bash +# Dry-run: list open PRs +gh pr list --state open --json number,title,author,isDraft,mergeable,statusCheckRollup --limit 10 + +# Check current gate status +gh pr view 123 --json statusCheckRollup,mergeable,reviewDecision + +# Verify careful-mode lists +cat ~/.claude/careful-mode-refuse.txt # if using careful-bash +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..3859041 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# molecule-workflow-triage + +Molecule AI plugin. Install via the Molecule AI platform plugin system. + +## Usage + +### In org template (org.yaml) +```yaml +plugins: + - molecule-workflow-triage +``` + +### From URL (community install) +``` +github://Molecule-AI/molecule-ai-plugin-molecule-workflow-triage +``` + +## License +Business Source License 1.1 — © Molecule AI. diff --git a/adapters/__init__.py b/adapters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/adapters/claude_code.py b/adapters/claude_code.py new file mode 100644 index 0000000..cc58993 --- /dev/null +++ b/adapters/claude_code.py @@ -0,0 +1,2 @@ +"""Claude Code adaptor — uses the generic rule+skill+hooks installer.""" +from plugins_registry.builtins import AgentskillsAdaptor as Adaptor # noqa: F401 diff --git a/commands/triage.md b/commands/triage.md new file mode 100644 index 0000000..7f45299 --- /dev/null +++ b/commands/triage.md @@ -0,0 +1,50 @@ +--- +name: triage +description: Run a full PR-triage cycle (gates 1-7 + code-review + merge if green). Equivalent to one cron tick, on demand. +--- + +# /triage + +Manual invocation of the hourly PR-triage flow. Use when: +- You want to clear backlog faster than the hourly cadence +- You're testing a change to the triage prompt itself +- A scheduled cron has died and the queue is backing up + +## Steps + +### Step 0 — Activate guards + replay learnings +1. `Skill careful-mode` — load REFUSE/WARN/ALLOW lists. +2. Read last 20 lines of cron-learnings JSONL (workspace memory dir). + +### Step 1 — List +``` +gh pr list --state open --json number,title,author,isDraft,mergeable,statusCheckRollup +gh issue list --state open --json number,title,assignees,labels +``` + +### Step 2 — 7-gate verification per PR +- Gate 1 CI · Gate 2 build · Gate 3 tests · Gate 4 security · Gate 5 design · Gate 6 line review · Gate 7 Playwright if UI +- Supplement A: `Skill code-review` +- Supplement B: `Skill cross-vendor-review` on noteworthy PRs (auth/billing/data-deletion/migration/large-blast-radius) + +### Step 2a — Mechanical fixes only +Fix on-branch + commit `fix(gate-N): ...` + push + poll CI. NEVER fix logic / design / auth issues. + +### Step 2b — Merge +All gates pass + 0 🔴 from code-review + cross-vendor agreement → `gh pr merge N --merge --delete-branch`. Merge-commit only. + +### Step 3 — Docs sync after any merge +`Skill update-docs` — measure test counts, don't guess. + +### Step 4 — Issue pickup (cap 2) +For each candidate: gates I-1..I-6, self-assign, branch, implement, draft PR, run `Skill llm-judge` against issue body + PR diff. Mark ready only if score >= 4. + +### Step 5 — Status report + cron-learnings +Report includes every subsection ("none" if empty). Then append 1-3 lines to cron-learnings JSONL. + +## Standing rules (inviolable) +- Never push to main · Merge-commits only +- careful-mode REFUSE list ALWAYS blocks +- code-review 🔴 ALWAYS blocks merge +- cross-vendor disagreement on noteworthy PR escalates to user +- llm-judge ≤ 2 blocks marking a draft PR ready diff --git a/known-issues.md b/known-issues.md new file mode 100644 index 0000000..d4c0cef --- /dev/null +++ b/known-issues.md @@ -0,0 +1,49 @@ +# Known Issues + +Active and recently resolved issues for `molecule-workflow-triage`. + +--- + +## Active Issues + +*(None currently open. File an issue if you encounter a problem.)* + +--- + +## Known Gotchas + +### `/triage` requires several sibling plugins to be useful + +**Severity:** Medium +**Detail:** `/triage` calls `code-review`, `cross-vendor-review`, `llm-judge`, `cron-learnings`, and `update-docs` as sub-steps. If those plugins are not installed, the triage will fail or degrade gracefully depending on the skill. + +**Workaround:** Install all recommended plugins (see CLAUDE.md) before enabling `/triage` in a workspace. + +--- + +### Gate 4 (security) may conflict with careful-mode REFUSE list + +**Severity:** Low +**Detail:** If a PR touches code that is on the `careful-mode` REFUSE list, Gate 4 (security) will flag it AND careful-mode will refuse to operate on the file. This is intentional but can be confusing — both are blocking. + +--- + +### Issue pickup cap of 2 may leave backlog + +**Severity:** Informational +**Detail:** If many issues are backlogged, `/triage` will only self-assign and begin work on 2 per run. Use multiple `/triage` invocations or increase the cap in a custom workspace configuration. + +--- + +### Mechanical fix commits do not trigger re-review + +**Severity:** Low +**Detail:** Gate fix commits (`fix(gate-N): ...`) are pushed to the PR branch and CI is re-polled, but no review request is re-sent. If a reviewer has already approved, the existing approval stands. If a reviewer has not yet reviewed, they may miss the fix. + +**Workaround:** After a mechanical fix, manually post a review comment to re-engage reviewers if needed. + +--- + +## Recently Resolved + +*(None yet.)* diff --git a/plugin.yaml b/plugin.yaml new file mode 100644 index 0000000..76154df --- /dev/null +++ b/plugin.yaml @@ -0,0 +1,11 @@ +name: molecule-workflow-triage +version: 1.0.0 +description: Provides /triage slash command — full PR-triage cycle composing code-review, cross-vendor-review, cron-learnings. Recommends installing molecule-skill-code-review and molecule-skill-cron-learnings first. +author: Molecule AI +tags: [molecule, guardrails] + +runtimes: + - claude_code + +commands: + - triage diff --git a/runbooks/local-dev-setup.md b/runbooks/local-dev-setup.md new file mode 100644 index 0000000..8596f59 --- /dev/null +++ b/runbooks/local-dev-setup.md @@ -0,0 +1,119 @@ +# Runbook: Local Development Setup — github-app-auth Plugin + +This runbook covers setting up a local development environment for the +`github-app-auth` plugin (GitHub App installation-token injection). It is a Go +plugin, not a standard molecule plugin — there is no Python validate-plugin.py +step. + +--- + +## Prerequisites + +| Requirement | Version | Notes | +|---|---|---| +| Go | 1.25+ | Must match `go.mod` | +| Git | 2.40+ | | +| GitHub App | Installed on org | Requires App ID, installation ID, private key PEM | +| `gh` CLI | 2.40+ | Optional — for manual verification | +| molecule-monorepo | Local checkout | Required for `cmd/` and `pluginloader/` packages to compile | + +--- + +## Step 1 — Clone + +```bash +git clone https://github.com/Molecule-AI/molecule-ai-plugin-github-app-auth.git +cd molecule-ai-plugin-github-app-auth +``` + +--- + +## Step 2 — Private Key + +Generate or retrieve the GitHub App private key PEM file: + +```bash +# Downloaded from: GitHub App Settings → Keys → Generate a private key +# Save as: +cp ~/Downloads/molecule-ai.private-key.pem /secrets/github-app.pem +chmod 600 /secrets/github-app.pem +``` + +Required env vars for local development: + +```bash +export GITHUB_APP_ID=3398844 # Your App's numeric ID +export GITHUB_APP_INSTALLATION_ID=... # Shown on the installation page +export GITHUB_APP_PRIVATE_KEY_FILE=/secrets/github-app.pem +``` + +> **Security:** Never commit the PEM file. It is gitignored. Keep it on a +> tmpfs or an encrypted volume. + +--- + +## Step 3 — Local Platform Replace Directive + +The `go.mod` includes a `replace` directive pointing at a local platform checkout +(`../molecule-monorepo/platform`). For local development: + +```bash +# If you have molecule-monorepo checked out adjacent to this repo: +ls ../molecule-monorepo/platform # verify it exists + +# If not, remove the replace directive (needed for CI): +go mod edit -dropreplace github.com/Molecule-AI/molecule-monorepo/platform +go mod tidy +``` + +--- + +## Step 4 — Build and Test + +```bash +# Test the self-contained internal package (no platform dependency): +go test ./internal/... -race -count=1 + +# Vet: +go vet ./internal/... + +# Build the server example (requires the platform replace directive): +go build ./cmd/server-with-github-app/ -o /tmp/test-server +``` + +The `cmd/` and `pluginloader/` packages require the `molecule-monorepo/platform` +module — they are integration territory, validated by the platform's own CI +when the plugin is pulled in as a dependency. + +--- + +## Step 5 — Integration Test + +If running in a local platform instance: + +```bash +# Start the platform with the plugin loaded +docker compose up -d --build platform + +# Verify the token is injected into a workspace container +docker exec ws-test printenv GITHUB_TOKEN +# Expected: ghs_... + +# Verify gh auth works inside the workspace +docker exec ws-test gh auth status +# Expected: Logged in to github.com as app/molecule-ai[bot] +``` + +--- + +## Common Issues + +| Symptom | Likely Cause | Fix | +|---|---|---| +| `go: module not found: github.com/Molecule-AI/molecule-monorepo/platform` | Local platform not checked out | `go mod edit -dropreplace ...` then `go mod tidy` | +| `githubapp: AppID is required` at boot | `GITHUB_APP_ID` not set | Set the env var; confirm the App ID from the GitHub App settings page | +| `crypto/rsa: key too small` error | Private key < 2048-bit | Re-generate the key (GitHub requires ≥ 2048-bit RSA keys) | +| `401 Unauthorized` in workspace | Installation token expired or App not installed | Check `ghs_` prefix in `GITHUB_TOKEN`; verify App is installed with correct permissions | +| `GraphQL` errors even though `gh auth status` OK | App lacks required OAuth scopes | Verify App has Contents: Write, Issues: Write, PRs: Write, Metadata: Read | +| `jwt: signature is invalid` | Clock skew > 60s | Sync system clock: `timedatectl set-ntp true` | +| Platform crashes on first workspace boot | PEM file missing or unreadable at startup | Verify: `test -r $GITHUB_APP_PRIVATE_KEY_FILE && echo OK` | diff --git a/skills/triage-workflow/SKILL.md b/skills/triage-workflow/SKILL.md new file mode 100644 index 0000000..59b855a --- /dev/null +++ b/skills/triage-workflow/SKILL.md @@ -0,0 +1,50 @@ +--- +name: triage-workflow +description: Orchestrates a full PR-triage cycle — 7-gate verification, code review, merge, docs sync, issue pickup. +--- + +# triage-workflow + +On-demand manual invocation of the PR-triage flow. Equivalent to one cron tick. + +## When to Use +- Clear PR backlog faster than the hourly cron cadence +- Test a change to the triage prompt itself +- A scheduled cron has died and the queue is backing up + +## How It Works + +### Activate guards + replay learnings +1. `Skill careful-mode` — load REFUSE/WARN/ALLOW lists +2. Read last 20 lines of cron-learnings JSONL (workspace memory dir) + +### PR verification (7 gates per PR) +1. CI status +2. Build +3. Tests +4. Security +5. Design review +6. Line review +7. Playwright (UI PRs only) + +Supplements: `Skill code-review`, `Skill cross-vendor-review` on noteworthy PRs. + +### Mechanical fixes only +Fix on-branch, commit `fix(gate-N): ...`, push, poll CI. Never fix logic/design/auth issues on a branch that isn't yours. + +### Merge conditions +All gates green + 0 🔴 from code-review + cross-vendor agreement → merge-commit only. + +### Docs sync after merge +`Skill update-docs` — measure test counts, don't guess. + +### Issue pickup (cap 2) +- Gates I-1..I-6 +- Self-assign, branch, implement, draft PR +- Run `Skill llm-judge` against issue body + PR diff +- Mark ready only if score >= 4 + +## Examples +``` +Skill triage-workflow +```