diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0370ef4..86a1392 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Personal Claude Code plugins, skills, and subagents", - "version": "0.25.0", + "version": "0.26.4", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/.gitignore b/.gitignore index 414fb9c..f4fd0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ node_modules/ # `duvet report --ci` verifies against. .duvet/reports/ .duvet/requirements/ + +# Transient agent scratch: reviewer wait logs and per-run worktrees. Both are +# produced by a single fx-dev run and are meaningless outside it. +.claude/team/waits/ +.claude/worktrees/ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 14470e2..ff6653d 100644 --- a/plugins/fx-dev/.claude-plugin/plugin.json +++ b/plugins/fx-dev/.claude-plugin/plugin.json @@ -1,5 +1,5 @@ { "name": "fx-dev", - "version": "5.0.0", + "version": "5.1.4", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coder/SKILL.md b/plugins/fx-dev/skills/coder/SKILL.md index 85cb120..5a2d96e 100644 --- a/plugins/fx-dev/skills/coder/SKILL.md +++ b/plugins/fx-dev/skills/coder/SKILL.md @@ -38,12 +38,13 @@ Pay attention to the user's framing. "Just fix the login bug real quick" is a bu 3. Plan logical PR structure if needed 4. Implement with tests 5. Run the local Codex review (`fx-dev:codex-review`) and converge it **before** opening the PR — it is the only local reviewer, and it is mandatory here exactly as in `fx-dev:dev` Step 4.5 -6. Create PR -7. Settle the automated reviewers with `fx-dev:copilot-review` and, where its GitHub App is installed, `fx-dev:coderabbit-review` -8. Address feedback -9. Launch a sub-agent with the pr-check-monitor skill for failing checks -10. Continue until ready for user review -11. Update issue to Done +6. Load `fx-dev:github` and follow its **PR conventions block** — conventional-commit title, no `#` or wave/phase wording in the title, and a body that is **never hard-wrapped** (one long line per paragraph; only the commit message wraps, at ~72 columns). Verify both before and after creating. +7. Create PR +8. Settle the automated reviewers with `fx-dev:copilot-review` and, where its GitHub App is installed, `fx-dev:coderabbit-review` +9. Address feedback +10. Launch a sub-agent with the pr-check-monitor skill for failing checks +11. Continue until ready for user review +12. Update issue to Done **When invoked from SDLC:** Stop after step 4 (implement with tests + commit). Do NOT create PRs or launch reviewers — the SDLC owns steps 5 onward. diff --git a/plugins/fx-dev/skills/github/SKILL.md b/plugins/fx-dev/skills/github/SKILL.md index e0cc709..f27ac23 100644 --- a/plugins/fx-dev/skills/github/SKILL.md +++ b/plugins/fx-dev/skills/github/SKILL.md @@ -192,6 +192,57 @@ Standardizes every skill on two canonical instruction files, with a pointer for This applies however the body is authored — heredoc, `--body-file`, or `gh api -F body=@file`. Tables, lists, and fenced code blocks keep their own line structure; the rule is about prose paragraphs. +### PR conventions block (paste verbatim into any agent prompt that may open or edit a PR) + +A convention that lives only in this skill does not survive delegation: an agent spawned with an ad-hoc prompt never loads it. Whenever you delegate PR creation — a `/team` coder opening its own PR, a fix agent editing a body, any sub-agent running `gh pr create` — paste this block into that agent's prompt verbatim. Quote it by name ("the github skill's PR conventions block") when referring to it from another skill. + +~~~markdown +### PR conventions (mandatory) +- TITLE: a conventional-commit subject. The **mechanical floor** is `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` — that regex checks the type prefix and nothing else, so passing it is necessary, not sufficient. +- TITLE: three more rules the floor regex cannot express. Two are checkable against the title (`grep -Eq '^[a-z]+(\(.+\))?!?: [a-z]'` must match — lowercase after the colon; `grep -Eq '\.$'` must NOT match — no trailing period). The third, **imperative mood** ("add", not "adds" or "added"), has no mechanical check: read the title and confirm it by eye. +- TITLE: no `#` unless it references a real existing PR/issue, and no wave/phase/step/batch or change-doc number. Squash-merge bakes the title into the default branch, where `#N` auto-links permanently. +- BODY: **never hard-wrapped.** GitHub reflows markdown to the reader's viewport, so write each paragraph as ONE long line and let it soft-wrap. Lists, tables and fenced code blocks keep their own line structure. This applies however the body is authored — heredoc, `--body-file`, or `gh api -F body=@file`. +- COMMIT MESSAGE: the opposite — wrap the body at ~72 columns, because git renders it as plain text. The rule follows the renderer, not the content. +- Verify before AND after creating: the title against every rule above, and the body with the command below. It judges only prose — fenced code, headings, blockquotes, tables, list items and their continuation lines are all skipped — and **exits 1 printing `HARD-WRAPPED`** when prose clusters in the 60-100 column band. Read its output; do not assume it passed. Fix with `gh pr edit --body-file ` and re-run. + +```bash +gh pr view --json body -q .body | awk ' + # Fenced code, CommonMark rules: a fence opens on ``` or ~~~ and closes only + # on the SAME character, at least as long as the fence that opened it — so a + # ```` block may legally contain a ``` line without closing. + { + t = $0; sub(/^[[:space:]]+/, "", t) + if (t ~ /^```/ || t ~ /^~~~/) { + ch = substr(t, 1, 1); len = 0 + while (substr(t, len + 1, 1) == ch) len++ + if (!fence) { fence = ch; flen = len; next } + else if (ch == fence && len >= flen) { fence = ""; flen = 0; next } + } + } + fence { next } + /^[[:space:]]*$/ { next } # blank: does not end a list + /^( |\t)/ { next } # indented code block + /^[[:space:]]*#/ { list = 0; next } # heading + /^[[:space:]]*>/ { list = 0; next } # blockquote + /^[[:space:]]*\|/ { list = 0; next } # table row + /^[[:space:]]*([-*+]|[0-9]+[.)])[[:space:]]/ { list = 1; next } # list marker line + /^[[:space:]]/ { if (list) next } # continuation, ONLY inside a list + { list = 0; n++; if (length($0) >= 60 && length($0) <= 100) w++ } + END { + if (n == 0) { print "no prose lines to check"; exit 0 } + printf "prose lines: %d; in the 60-100 col hard-wrap band: %d\n", n, w + if (w * 2 > n) { print "HARD-WRAPPED - rewrite each paragraph as ONE long line"; exit 1 } + print "OK - prose is not hard-wrapped" + }' +``` +~~~ + +**That command is the canonical "Mechanical body check"**, and `fx-dev:team` merge gate 5c refers to it by that name. Run it yourself after creating or editing any PR body, not only when delegating. + +**It is a heuristic, not a Markdown parser, and that is deliberate.** It follows CommonMark on the two things that actually bite — a fence closes only on the same character at a length at least its opening, and an indented line is a continuation only inside a list — and it guesses at the rest. Treat a `HARD-WRAPPED` verdict as a prompt to *read* the body, not as proof. If you have read it and the prose genuinely is one line per paragraph, say so in your report and move on: **do not rewrite correct prose to satisfy the checker**, and do not extend the awk to chase a further Markdown construct. The supply of constructs does not run out, and the rule the gate enforces is the one in prose above it. + +Do not substitute `awk '{print length}' | sort -rn | head -3`. The three longest lines in a body are usually a table row or a code line, both exempt, so it reports a healthy number for a body whose prose is entirely hard-wrapped. + **Use Conventional Formats:** - **Commit messages**: Follow conventional commit format (`feat:`, `fix:`, `refactor:`, `docs:`, etc.) - **PR titles**: MUST use conventional commit format — `type(scope): description` (e.g., `feat: add user authentication`, `fix(api): handle null token`). **BLOCKING**: on squash-merge the PR title becomes the commit subject, so a plain prose title (no `type:` prefix) permanently pollutes a conventional-commit history. **Canonical check** — every PR title, no matter who creates it (pr-preparer, the `/dev` workflow, or a `/team` coordinator running `gh pr create` directly), MUST match this regex; verify before creating AND before merging: diff --git a/plugins/fx-dev/skills/pr-preparer/SKILL.md b/plugins/fx-dev/skills/pr-preparer/SKILL.md index 3b2ee33..5f07194 100644 --- a/plugins/fx-dev/skills/pr-preparer/SKILL.md +++ b/plugins/fx-dev/skills/pr-preparer/SKILL.md @@ -56,6 +56,41 @@ Then, your primary responsibilities: **Never hard-wrap the description.** GitHub reflows markdown to the reader's viewport, so hard-wrapping prose at 80 columns (or any column) only renders ragged and re-wraps badly on narrow screens. Write each paragraph as ONE long line and let it soft-wrap. Commit messages are the opposite — those stay wrapped at ~72 columns, because git renders them as plain text. See the `fx-dev:github` skill's "Never hard-wrap anything GitHub renders as markdown". + **Verify it, do not merely intend it.** After creating or editing the PR, run the canonical body check from the `fx-dev:github` skill's "Mechanical body check" — restated here in full so it reaches you even when that skill is not loaded — and read its output: + + ```bash + gh pr view --json body -q .body | awk ' + # Fenced code, CommonMark rules: a fence opens on ``` or ~~~ and closes only + # on the SAME character, at least as long as the fence that opened it — so a + # ```` block may legally contain a ``` line without closing. + { + t = $0; sub(/^[[:space:]]+/, "", t) + if (t ~ /^```/ || t ~ /^~~~/) { + ch = substr(t, 1, 1); len = 0 + while (substr(t, len + 1, 1) == ch) len++ + if (!fence) { fence = ch; flen = len; next } + else if (ch == fence && len >= flen) { fence = ""; flen = 0; next } + } + } + fence { next } + /^[[:space:]]*$/ { next } # blank: does not end a list + /^( |\t)/ { next } # indented code block + /^[[:space:]]*#/ { list = 0; next } # heading + /^[[:space:]]*>/ { list = 0; next } # blockquote + /^[[:space:]]*\|/ { list = 0; next } # table row + /^[[:space:]]*([-*+]|[0-9]+[.)])[[:space:]]/ { list = 1; next } # list marker line + /^[[:space:]]/ { if (list) next } # continuation, ONLY inside a list + { list = 0; n++; if (length($0) >= 60 && length($0) <= 100) w++ } + END { + if (n == 0) { print "no prose lines to check"; exit 0 } + printf "prose lines: %d; in the 60-100 col hard-wrap band: %d\n", n, w + if (w * 2 > n) { print "HARD-WRAPPED - rewrite each paragraph as ONE long line"; exit 1 } + print "OK - prose is not hard-wrapped" + }' + ``` + + It exempts fenced code, headings, blockquotes, tables, and list items with their continuation lines, and judges only prose. It **exits 1 and prints `HARD-WRAPPED`** when prose clusters in the 60-100 column band. If it does, rewrite each paragraph as one long line and `gh pr edit --body-file `, then run it again. It is a heuristic, not a Markdown parser: if you have read the body and its prose genuinely is one line per paragraph, say so and move on rather than rewriting correct prose to satisfy the checker. + 5. **Check Compliance**: Verify adherence to: - Project-specific guidelines from AGENTS.md files - Global coding standards and architectural decisions diff --git a/plugins/fx-dev/skills/team/SKILL.md b/plugins/fx-dev/skills/team/SKILL.md index 8f743ab..dc81093 100644 --- a/plugins/fx-dev/skills/team/SKILL.md +++ b/plugins/fx-dev/skills/team/SKILL.md @@ -222,7 +222,11 @@ Two constraints worth knowing rather than rediscovering: When you spawn the coder for the FINAL piece of a change, your prompt MUST include: "This is the final implementing PR for . In the same commit, flip `**Status:** draft` → `**Status:** complete` in `docs/changes/-*.md` AND flip `status: draft` → `status: complete` for that change's entry in `docs/index.yml`. Sync `docs/index.md` if present." For every NON-final coder on the same change, your prompt MUST include: "Leave the change-doc `**Status:**` field and `docs/index.yml` entry untouched — the final PR flips them." This split prevents rebase-conflict storms across multi-PR changes and ensures the final PR carries the Status flip atomically. -**PR creation** → Either do it yourself via `gh pr create` or spawn a focused PR preparer agent. Load `fx-dev:github` skill first. **⛔ If you create the PR yourself, the `--title` MUST be a conventional-commit subject — `type(scope): description` — matching the canonical regex `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` (see the github skill's "Use Conventional Formats"). Do NOT write a prose title; running `gh pr create` directly does NOT exempt you from the conventional-commit rule. Verify the title against the regex before AND after creation.** (Prose titles the coordinator wrote directly — bypassing pr-preparer — are exactly how non-conventional titles have slipped onto `main`.) +**⛔ Every spawn prompt that may open or edit a PR MUST carry the PR conventions block verbatim (BLOCKING).** Load `fx-dev:github` BEFORE you author your first spawn prompt, and paste its **"PR conventions block"** into the prompt of every agent that might run `gh pr create` or `gh pr edit` — coder, fix agent, PR preparer, anything. A convention that lives only in a skill nobody loads does not survive delegation: a spawned agent inherits your prompt, not your skills. + +This is not hypothetical. In an observed run, all three coders received the TITLE rule — because this skill restates it inline below and gates it at merge — and none received the BODY rule, which lives only in `fx-dev:github`. All three PRs shipped hard-wrapped bodies that render ragged on GitHub, while PRs prepared through `fx-dev:pr-preparer` in the same repo did not. Restating a rule here is what makes it propagate; anything you do not restate or gate, you will not get. + +**PR creation** → Either do it yourself via `gh pr create`, spawn a focused PR preparer agent, or let a coder open its own PR. Load `fx-dev:github` skill first, and pass its PR conventions block into the prompt whenever you delegate. **⛔ Whoever creates the PR — you or an agent you spawned — the `--title` MUST be a conventional-commit subject — `type(scope): description` — matching the canonical regex `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` (see the github skill's "Use Conventional Formats"). Do NOT write a prose title; running `gh pr create` directly does NOT exempt you from the conventional-commit rule. Verify the title against the regex before AND after creation.** (Prose titles the coordinator wrote directly — bypassing pr-preparer — are exactly how non-conventional titles have slipped onto `main`.) **Review and CI steps** (Copilot review, CodeRabbit review, CI monitoring, feedback resolution) → **Handle these DIRECTLY as the coordinator.** These are lightweight skill/command invocations that must not be delegated. **Pass the STEP 0 Scope Brief into every reviewer invocation that accepts one, and apply it when triaging every reviewer that does not** (Copilot and the CodeRabbit GitHub App accept nothing). A finding covered by the brief's out-of-scope list is recorded as deferred with the covering exclusion — never silently fixed, never silently dropped, and never a reason to widen a teammate's PR. Use each reviewer's waiter or read-only inspection first, classify and deduplicate findings under `fx-dev:dev` Step 2.5, then invoke feedback resolvers only for the classified disposition. Never let a resolver implement unclassified feedback or modify task trackers for deferred feedback. @@ -300,6 +304,7 @@ duvet# A pull request MUST NOT be merged while any review thread on it from a co | 4 | **Spec task marked complete** | Check via project-management skill | YES | | 5 | **PR description is clear** | Read PR body | YES | | 5b | **PR title is clean AND conventional** | Title (a) is a conventional-commit subject — run the canonical check from the `fx-dev:github` skill's "Use Conventional Formats" (a plain prose title with no `type:` prefix FAILS) — AND (b) has NO stray `#` (only a real PR/issue ref) and NO wave/phase/step/change-doc number. Fix with `gh pr edit --title "type(scope): …"` before merge — squash bakes the title into `main` | YES | +| 5c | **PR body is NOT hard-wrapped** | Run the canonical **"Mechanical body check"** from the `fx-dev:github` skill — it exempts lists, tables and code blocks, judges only prose, and exits 1 printing `HARD-WRAPPED` when prose clusters in the 60-100 column band. Do NOT substitute a `sort -rn \| head` on line lengths: the longest lines are usually exempt ones, so it passes a body whose prose is entirely wrapped. Fix with `gh pr edit --body-file ` and re-run before merging | YES | | 6 | **Browser verification completed** | Spawn a verify agent if needed (see below) | YES | ### ⛔ Reviewer Gates (Gates 2 + 2b) — CRITICAL @@ -419,6 +424,7 @@ When all tasks are complete and all PRs merged: - **NEVER write code yourself** — all implementation goes through coder agents - **NEVER create branches or commits** — coder agents handle this - **NEVER delegate the full SDLC to a single agent** — agents cannot spawn sub-agents, so they will inline everything and skip later steps +- **ALWAYS paste the `fx-dev:github` PR conventions block into every spawn prompt whose agent may open or edit a PR** — load that skill before authoring your first prompt. A spawned agent inherits your prompt, not your skills; a rule you do not restate is a rule that does not reach it. - **NEVER skip PR inspection** — every PR gets reviewed before marking ready - **NEVER merge without completing the MERGE GATE CHECKLIST** — every gate must pass, every time, for every PR - **NEVER merge without Copilot review** — always invoke `fx-dev:copilot-review` yourself. No exceptions.