From 25fbe88b6dbfc2909d70384357b70086c2d60e36 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 22:29:45 +0000 Subject: [PATCH 01/48] feat(fx-dev): add a materiality bar so reviews converge on what matters Review loops did not terminate. Every observation was treated as a finding, so each pass fixed what the last one raised and surfaced a fresh crop of smaller ones: the findings got less important, the passes kept costing the same, and the loop ended only when someone gave up. A recent docs-only PR ran twelve Codex passes with findings per pass of 12, 13, 10, 5, 5, 5, 3, 4, 3, 3, 4, 6 -- it plateaued rather than converged, and the tail was list-completeness nitpicks plus one already-settled decision being re-argued in new words each round. Scope answers what to review. Nothing answered what is worth reporting or when to stop, so this adds a materiality bar to the shared scope contract that fourteen skills already import, and wires the five review skills to it. A finding is worth reporting when acting on it would change what the artifact does or how a competent reader acts on it. Material and substantive findings are reported individually and block convergence; immaterial ones go in a single non-blocking closing note and never trigger another pass. The test is "if this shipped uncorrected, what breaks" -- if the answer is "nothing, it is just not as good", it is immaterial. Three shapes are explicitly not findings, generalised from the observed failure rather than baked in as examples: an entry missing from a list the artifact itself declares non-exhaustive (assess the rule, since the supply of such entries never runs out), a decision the artifact records with its rationale including "unknown, gated on X" (escalate once, do not re-argue), and a limit the artifact admits and gates. Convergence is redefined as "a pass with no material or substantive findings" rather than zero output, with the shape of the trend as the signal: falling with new categories means keep going, flat or oscillating and immaterial means stop, the same disagreement twice means escalate to a human. Reviewers report the per-pass trend when they stop so it cannot be taken on trust. Per skill: codex-review gains a required convergence-pass prompt block that tells Codex the bar and the settled ground up front rather than filtering its output afterwards; copilot-review and resolve-pr-feedback carry the loop-specific trap that every push re-opens the gate, so editing for an immaterial finding costs a wait cycle and manufactures the next round's input; coderabbit-review notes its own severity labels are an input to the judgment, not a substitute; pr-reviewer must state counts per tier; and dev requires a finding to clear the bar before it can be classified required-by-contract at all. Also adds "verify before fixing" to the reviewer skills. A finding's premise can be wrong, and changing working code to satisfy a misreading is worse than leaving the finding unaddressed. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 14 +++-- plugins/fx-dev/skills/codex-review/SKILL.md | 54 ++++++++++++++++--- plugins/fx-dev/skills/copilot-review/SKILL.md | 11 ++++ plugins/fx-dev/skills/dev/SKILL.md | 2 + .../skills/dev/references/scope-contract.md | 47 ++++++++++++++-- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 28 ++++++++++ .../skills/resolve-pr-feedback/SKILL.md | 10 ++++ 9 files changed, 154 insertions(+), 16 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f937363..5c54262 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.21.2", + "version": "0.22.0", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index d0c1a08..2bcbc1f 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": "3.8.2", + "version": "3.9.0", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index ad2c02f..80cb1a8 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -100,16 +100,22 @@ Use `cr review --agent --base main` to scope to the branch's diff against `main` Treat findings like self-review feedback: +- **Triage by the materiality bar first** (`fx-dev/skills/dev/references/scope-contract.md`). Material and substantive findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the project doesn't care about. +- **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the tree; when it does not hold, reject the finding with the evidence rather than changing working code to satisfy a misreading. - There are no PR threads to resolve here โ€” this is local. Resolution = the code is fixed (or the finding is a deliberate non-issue). -### Step 3: Re-run until clean (REQUIRED) +### Step 3: Re-run until it converges (REQUIRED) -Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until the review reports no actionable findings.** +Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass produces no material or substantive findings.** -- **Cap at 4 iterations.** If CodeRabbit keeps flagging the same design decision after 4 passes, that is a human call, not more code edits โ€” escalate to the user. +**Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. + +- **Cap at 4 iterations.** If CodeRabbit keeps flagging the same design decision after 4 passes, that is a human call, not more code edits โ€” escalate it **by name** and stop. +- Watch the shape, not just the count: falling with new categories โ†’ keep going; flat or oscillating and all immaterial โ†’ converged; the same disagreement twice โ†’ escalate. - **Rate-limit exception:** stop immediately on throttling; do not consume iterations waiting for cooldowns. +- When you stop, report the per-pass trend and whether the last round's fixes were themselves reviewed. ### Step 4: Open the PR when clean or correctly degraded @@ -183,6 +189,8 @@ If this count is 0 AND the CodeRabbit check is `success`, the gate is PASSED. **Cap the loop at 4 iterations** โ€” if CodeRabbit is still posting new feedback after 4 wait+resolve cycles, escalate to the user. Almost always this means CodeRabbit and the codebase disagree on a design decision that needs human input. +**Threads must all be resolved, but resolution is not the same as a fix.** An immaterial thread is resolved by replying with the reason it is not being actioned โ€” the gate is zero *unresolved* threads, not zero observations acted on. Re-pushing for another CodeRabbit pass to chase immaterial items is exactly the churn the materiality bar exists to stop. + ## Concurrency With Other Reviewers (Mode 2) Mode 2 can run **in parallel** with `fx-dev:copilot-review` and any future automated-reviewer skills. The SDLC step that gates merge on automated review should: diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index ec13193..eaaac91 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -253,30 +253,70 @@ than waiting it out. ### Step 2: Resolve every actionable finding +- **Triage by the materiality bar first** (see + `fx-dev/skills/dev/references/scope-contract.md`). Material and substantive + findings are fixed. Immaterial ones โ€” wording, formatting, a count nothing keys + on, an entry missing from a list the artifact declares non-exhaustive โ€” are + collected into one closing note and MUST NOT drive another iteration. - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the project doesn't care about. There are no PR threads to resolve here (this is local); resolution = the code is fixed (or the finding is a deliberate non-issue). +- **Verify before fixing.** A reviewer's premise can be wrong. When a finding + rests on a claim about the tree, check it โ€” and when it does not hold, reject + the finding with the evidence and put it in the next prompt's do-not-re-report + list. Fixing a phantom finding is worse than leaving it: it changes working + artifacts to satisfy a misreading. - **Incorrect findings** โ€” when Codex flags a pattern that is a deliberate project convention, document it in `REVIEW.md` at the repo root, the same as the PR feedback resolvers do. One entry stops Codex, Copilot, CodeRabbit, and Claude Code Review from raising it again. Never write it into the obsolete `.github/copilot-instructions.md`. -### Step 3: Re-run until clean (REQUIRED) +### Step 3: Re-run until it converges (REQUIRED) Run the review again after fixes, **carrying the same Scope Brief prompt plus anything newly established**. Note the iteration number in the prompt and add facts verified since the last pass, so Codex does not relitigate settled ground. -**Repeat Steps 1 โ†’ 2 until the review reports no actionable findings.** +**Repeat Steps 1 โ†’ 2 until a pass produces no material or substantive findings** +โ€” per the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. + +**Converged does NOT mean zero output.** Codex will keep producing immaterial +observations indefinitely; waiting for silence spends full review cycles on +wording. Stop when what remains would change nothing if it shipped uncorrected, +and list those items once, non-blocking. + +**Tell Codex the bar and the settled ground in the prompt**, so it spends the +pass where it pays. Every re-run prompt MUST carry, in addition to the Scope +Brief: + +``` +CONVERGENCE PASS . Prior passes found issues; all fixed except . Do not re-report them. + +Report only findings that would change behaviour, break a build or test, make +the artifact unimplementable, or state something false. Wording, formatting, +and counts nothing keys on: one closing note, not findings. + +Where this artifact declares a list illustrative and a rule authoritative, +assess the RULE. A further missing list entry is not a finding. + +Where it records a decision with its rationale โ€” including "unknown, gated on +X" โ€” that is settled. If you believe it is wrong, say so once as an escalation; +do not re-argue it. + +If the artifact is internally consistent and matches the tree, say so plainly. +``` - **Cap at 4 iterations.** If Codex keeps flagging the same design decision after - 4 passes, that is a human call, not more code edits โ€” escalate to the user. -- The cap targets a reviewer stuck on one disagreement. When each pass is instead - surfacing genuinely new material and the finding count is falling, say so when - you stop, and tell the user that the last round's fixes went unverified so they - can ask for one more pass. + 4 passes, that is a human call, not more edits โ€” escalate it **by name** and + stop. +- Watch the shape, not just the count. Falling with new categories each pass โ†’ + keep going. Flat or oscillating and all immaterial โ†’ converged, stop. The same + disagreement twice โ†’ escalate. +- When you stop, **report the per-pass trend** (`9, 4, 1, 0 material`) and say + whether the last round's fixes were themselves reviewed. ### Step 3.5: Report out-of-scope findings, never silently apply them diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index 67ae240..bcd538f 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -99,6 +99,17 @@ not handed it. Implementing its out-of-scope suggestions is scope creep with a reviewer's name on it. +Triage by **materiality** as well as scope โ€” see the materiality bar in the same +reference. An in-scope finding that would change nothing if it shipped +uncorrected is **resolved by replying with that reasoning**, not by editing. The +gate is zero *unresolved* Copilot threads, not zero observations acted on. + +This matters more here than anywhere else in the loop: **every push re-opens the +gate.** Copilot must then re-review the new head (Step 5), so editing for an +immaterial finding costs a full wait cycle and produces a fresh commit for it to +comment on. Push fixes for material and substantive findings; reply-and-resolve +the rest without a commit. + ## When to Use - After creating a PR (SDLC Step 6.3) diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 243802f..41d2ab5 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -316,6 +316,8 @@ Fix only findings classified `required-by-contract` or `regression-caused-by-cha Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeated or reworded findings against the ledger; they do not start a new cycle. When `/dev` invokes reviewer subskills, this contract classification and bounded stopping policy takes precedence over generic instructions to resolve every actionable finding or rerun until clean. +**A finding must clear the materiality bar before it can be classified at all** (`references/scope-contract.md`). An observation that would change nothing if it shipped uncorrected is neither `required-by-contract` nor `regression-caused-by-change` โ€” it is recorded in the ledger as immaterial and never triggers a rerun. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. + #### PR-Ready Stopping Condition Proceed to Step 5 when all of the following are true: diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 157db6c..993163d 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -1,13 +1,14 @@ # The Scope Contract -Canonical definition of the **Scope Brief** and the **sprawl stop rule**. Both are mandatory across fx-dev workflow and review skills. +Canonical definition of the **Scope Brief**, the **materiality bar**, and the **sprawl stop rule**. All three are mandatory across fx-dev workflow and review skills. -Two failures motivate this document: +Three failures motivate this document: 1. **Reviewers flagging work nobody asked for.** A reviewer handed a bare diff has no idea what was requested. It reports missing implementation for a docs-only change, missing tests for a spec, or absent dependencies that a later phase adds. Every one of those is noise the operator must hand-filter, and each round of noise costs a full review cycle. -2. **Work quietly outgrowing the request.** A user says "just fix the typo real quick" and gets a refactor. The user's own phrasing is the scope signal, and it is routinely discarded the moment the first sub-agent is launched. +2. **Reviewers never finishing.** A review loop that treats every observation as a finding does not converge: each pass fixes what the last one raised and surfaces a fresh crop of smaller ones. The findings get less important, the passes keep costing the same, and the loop ends only when someone gives up. Scope answers *what to review*; materiality answers *what is worth reporting and when to stop*. +3. **Work quietly outgrowing the request.** A user says "just fix the typo real quick" and gets a refactor. The user's own phrasing is the scope signal, and it is routinely discarded the moment the first sub-agent is launched. -Both have the same root cause: the user's original words stop travelling. The Scope Brief makes them travel. +The first and third have the same root cause: the user's original words stop travelling. The Scope Brief makes them travel. The second needs its own rule, below. ## The Scope Brief @@ -69,6 +70,44 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i **Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, data-loss, and correctness problems inside the change are always in scope, whatever the brief says. If a reviewer flags something excluded and is *right* โ€” the exclusion was wrong โ€” fix the work and correct the brief. +## The materiality bar + +**A finding is worth reporting when acting on it would change what the artifact does, or change how a competent reader acts on it. Everything else is an observation, not a finding.** + +This is not a licence to ignore problems. It is a ranking rule: report what clears the bar, and put what does not in one closing note rather than in the blocking list. + +### The bar + +| Tier | Examples | Treatment | +|---|---|---| +| **Material** | Wrong behaviour, data loss, security, a build/CI/test that will fail, a contradiction that makes the artifact unimplementable, a stated fact that is false | Report individually. Blocks convergence. | +| **Substantive** | A genuine ambiguity a reader could act on two ways; a missing step that would be discovered late and cost a cycle | Report individually. Blocks convergence. | +| **Immaterial** | Wording that is merely improvable; a count off by one where nothing keys on the count; a list entry missing from a list nothing enumerates exhaustively; formatting; a synonym that reads better | **One closing note, unnumbered, non-blocking.** Never a separate finding, never a reason for another pass. | + +When unsure which tier something is, ask: *if this shipped uncorrected, what breaks?* If the honest answer is "nothing, it is just not as good as it could be", it is immaterial. + +### Three things that are not findings + +**A missing entry in a list the artifact declares non-exhaustive.** If a document says "for example" or "this list is illustrative; the rule is authoritative", then supplying entry N+1 does not improve it โ€” the rule already covers N+1. Assess whether the *rule* is correct and sufficient. Reporting further missing entries against a declared-open list is the single most common way a review loop fails to terminate, because the supply of such entries never runs out. + +**A decision already made, recorded, and reasoned.** Once an artifact states a decision with its rationale โ€” including a rationale that says "we do not know, and here is how we will find out" โ€” re-arguing it is not a review finding. If you believe the decision is *wrong*, say so once, plainly, as a single escalation to the human; do not re-raise it on the next pass in different words. A decision re-litigated across passes is a signal that it needs a person, not another round. + +**An artifact that admits a limit.** "This is verified by X at implementation time", "this is an open question gated on Y" โ€” those are dispositions, not gaps. Check the gate is real and sequenced before the thing that depends on it; do not report the limit itself. + +### Convergence + +**A review has converged when a pass produces no material or substantive findings โ€” not when it produces zero output.** Zero is usually unreachable and waiting for it burns cycles on immaterial churn. + +Track findings per pass and watch the shape, not just the count: + +- **Falling, with new categories each pass** โ€” keep going. The review is still working. +- **Flat or oscillating, all immaterial** โ€” converged. Stop, and say so: report the count trend and what remains below the bar. +- **The same disagreement in successive passes** โ€” stop. That is a human decision, not a review outcome. Escalate it by name. + +Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 material. Stopping โ€” three immaterial wording items remain, listed below."* + +**State honestly what the last pass did not cover.** If you stop after applying fixes that were never themselves reviewed, say so. + ## The sprawl stop rule **When work outgrows the request, stop and tell the user.** Do not silently deliver more than was asked. diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index f10b6c6..c4d1872 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -39,6 +39,34 @@ Then review within it: have built. "This should also handle X" is out of scope unless the request, the spec, or a genuine regression demands it. +## Rank by materiality, and say where the bar fell + +Scope decides whether a finding is the author's problem. **Materiality decides +whether it is worth their time** โ€” see the materiality bar in the same reference. + +- **Report material and substantive findings individually.** Wrong behaviour, + data loss, security, a build or test that will fail, a contradiction that makes + the change unimplementable, a stated fact that is false, or a genuine ambiguity + a reader could act on two ways. +- **Collect everything else into one closing note**, unnumbered and explicitly + non-blocking. Wording, formatting, naming preference, a count nothing keys on. +- When unsure of the tier, ask: *if this shipped uncorrected, what breaks?* If + the honest answer is "nothing, it is just not as good as it could be", it + belongs in the closing note. + +Three things are **not findings**, and raising them is how a review becomes +noise the author learns to skim: + +- a missing entry in a list the change itself declares illustrative โ€” assess the + rule instead, since the supply of such entries never runs out; +- a decision the change records with its rationale โ€” if you think it is wrong, + say so once as an escalation rather than re-arguing it; +- a limit the change admits and gates ("verified at implementation time", + "open question gated on X") โ€” check the gate is real and sequenced, and move on. + +**A review that reports twenty things equally has reported nothing.** State the +count at each tier so the author knows what to act on first. + ## CRITICAL: Project-Specific Rules (Read First!) **BEFORE reviewing any code, you MUST:** diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index f915ae6..063fb95 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -19,6 +19,16 @@ Pass it into every resolver you invoke, and classify each finding before acting: Security, data-loss, and correctness problems **inside** the change are always in scope, whatever the brief says. Resolving out-of-scope suggestions is scope creep with a reviewer's name on it. +## Also triage by materiality + +Scope decides whether a finding is *ours*. Materiality decides whether it is *worth a fix* โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. + +An in-scope finding that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is **resolved by replying with that reasoning**, not by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. + +This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for material and substantive findings; reply-and-resolve the rest in the same cycle. + +**Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the code, and when it does not hold, reply with the evidence and resolve rather than changing working code to satisfy a misreading. + ## WHEN TO USE THIS SKILL **USE THIS SKILL** when ANY of the following occur: From 4b81909c79f8b905805dc3bf54f1f830541c7a1e Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 22:43:39 +0000 Subject: [PATCH 02/48] fix(fx-dev): close four contradictions the materiality bar introduced MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex found four, all genuine contradictions between the new rules and instructions already in the same files. No wording nitpicks โ€” the new prompt structure worked. The codex-review rerun prompt listed the reportable tiers as an exhaustive "only": behaviour, build/test, implementability, truth. A credential, internal URL or private identifier leaked into documentation matches none of those โ€” removing it changes content, not behaviour โ€” so the prompt could have suppressed exactly the class REVIEW.md requires be flagged. Security, privacy and data-loss are now named in the prompt. "Falling with new categories means keep going" contradicted "a pass with no material findings means stop": five immaterial observations becoming three different immaterial observations is a fall, and the rule as written bought another pass for nitpicks. All three copies now count only the tiers that block. resolve-pr-feedback gained materiality triage at the top while its loop and success criterion still demanded zero new feedback, so an agent that correctly resolved immaterial threads by reply would loop or escalate anyway. Convergence there is now no new material or substantive findings plus every thread resolved on the reviewed head. pr-reviewer told reviewers to demote immaterial findings to a non-blocking note while also requiring every project-rule violation to be BLOCKING, permitting opposite verdicts on the same finding. Precedence is now explicit: the bar governs findings a reviewer originates from its own judgment, never rules the project wrote down. The project already decided those matter and that decision is not the reviewer's to re-make. --- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 15 +++++++++------ .../skills/dev/references/scope-contract.md | 4 ++-- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 2 ++ .../fx-dev/skills/resolve-pr-feedback/SKILL.md | 4 ++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 80cb1a8..79b3334 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -113,7 +113,7 @@ Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass p **Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. - **Cap at 4 iterations.** If CodeRabbit keeps flagging the same design decision after 4 passes, that is a human call, not more code edits โ€” escalate it **by name** and stop. -- Watch the shape, not just the count: falling with new categories โ†’ keep going; flat or oscillating and all immaterial โ†’ converged; the same disagreement twice โ†’ escalate. +- Watch the shape, and count only the tiers that block: material or substantive findings still arriving โ†’ keep going; a pass with none โ†’ converged, however many immaterial observations it produced; the same disagreement twice โ†’ escalate. - **Rate-limit exception:** stop immediately on throttling; do not consume iterations waiting for cooldowns. - When you stop, report the per-pass trend and whether the last round's fixes were themselves reviewed. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index eaaac91..8edba51 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -295,9 +295,11 @@ Brief: CONVERGENCE PASS . Prior passes found issues; all fixed except . Do not re-report them. -Report only findings that would change behaviour, break a build or test, make -the artifact unimplementable, or state something false. Wording, formatting, -and counts nothing keys on: one closing note, not findings. +Report findings that would change behaviour, break a build or test, make the +artifact unimplementable, state something false, or expose a security, privacy, +or data-loss problem โ€” including a leaked credential, internal URL, or private +identifier in documentation or examples. Wording, formatting, and counts nothing +keys on: one closing note, not findings. Where this artifact declares a list illustrative and a rule authoritative, assess the RULE. A further missing list entry is not a finding. @@ -312,9 +314,10 @@ If the artifact is internally consistent and matches the tree, say so plainly. - **Cap at 4 iterations.** If Codex keeps flagging the same design decision after 4 passes, that is a human call, not more edits โ€” escalate it **by name** and stop. -- Watch the shape, not just the count. Falling with new categories each pass โ†’ - keep going. Flat or oscillating and all immaterial โ†’ converged, stop. The same - disagreement twice โ†’ escalate. +- Watch the shape, and count only the tiers that block. Material or substantive + findings still arriving โ†’ keep going. A pass with none โ†’ converged, however + many immaterial observations it produced. The same disagreement twice โ†’ + escalate. - When you stop, **report the per-pass trend** (`9, 4, 1, 0 material`) and say whether the last round's fixes were themselves reviewed. diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 993163d..e6a6327 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -100,8 +100,8 @@ When unsure which tier something is, ask: *if this shipped uncorrected, what bre Track findings per pass and watch the shape, not just the count: -- **Falling, with new categories each pass** โ€” keep going. The review is still working. -- **Flat or oscillating, all immaterial** โ€” converged. Stop, and say so: report the count trend and what remains below the bar. +- **Material or substantive findings still arriving, in new categories** โ€” keep going. The review is still working. +- **No material or substantive findings this pass** โ€” converged, whatever the immaterial count did. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. - **The same disagreement in successive passes** โ€” stop. That is a human decision, not a review outcome. Escalate it by name. Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 material. Stopping โ€” three immaterial wording items remain, listed below."* diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index c4d1872..c58eed4 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -89,6 +89,8 @@ count at each tier so the author knows what to act on first. 2. **Apply project rules as BLOCKING issues.** These files define project-specific requirements that override general best practices. Violations are BLOCKING, not suggestions. + **Precedence with the materiality bar:** a project-rule violation is blocking **by virtue of being a project rule** โ€” the bar does not filter it out. The project has already decided the rule matters; that decision is not yours to re-make per finding. The bar governs findings you originate from your own judgment, not rules the project wrote down. If a project rule genuinely produces noise, say so once as feedback on the rule, and still report the violation. + ### Vendor Code Reuse Check (BLOCKING) For projects with vendor submodules (e.g., `vendor/` directory): diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 063fb95..9d9285e 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces zero new feedback **on a head SHA that was actually reviewed** (verify: the newest Copilot review's `commit_id` equals `headRefOid`). Cap at 4 outer iterations and escalate to the user if not converged. +6. Stop when a pass produces **no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`) **and every automated thread on that head is resolved**. Immaterial threads resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. @@ -287,7 +287,7 @@ If unresolved threads remain, report which reviewers still have open feedback. 1. All unresolved automated review threads identified โ€” matched on the `copilot-pull-request-reviewer` login, **not** the bare `Copilot` โ€” **plus** any findings in the "Suppressed comments" block of every Copilot review **of the current head commit**, which produce no threads 2. Appropriate resolver skill(s) invoked (Copilot + CodeRabbit in parallel where applicable) -3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA** and produced zero new findings. That is the whole condition: do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence +3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, produced no new material or substantive findings, and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence 4. CodeRabbit's check is in a terminal passing state (or absent if not configured) 5. Final verification confirms all threads resolved 6. Summary output provided From 09e4c6609bf8f38d47bba73afcc92db781dc0c7f Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 22:53:00 +0000 Subject: [PATCH 03/48] fix(fx-dev): order the three filters and keep unsafe decisions blocking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 2 found three, down from four, all still substantive and all one family: precedence between scope, project rules, and materiality. The order is now defined once in the shared contract and referenced by the rest. Scope first โ€” an out-of-scope finding is deferred however material it looks in isolation, because a valid improvement to work the change deliberately did not do is still not this change's problem, and materiality must never promote something back into scope. Then contract: project rules and security or privacy invariants block by virtue of being rules the project wrote down. Materiality ranks only what survives both. Saying "triage by materiality first" had inverted this in codex-review and coderabbit-review, and the dev prefilter could demote a mandatory violation the reviewer had correctly marked blocking. The larger hole was the recorded-decision rule. As written it made any decision with a rationale a non-finding unconditionally, so an artifact that documented why it leaked a credential or lost data would have been escalated once and then allowed to converge. That rule now covers disagreements of preference only: if the decision itself is the defect โ€” security, privacy, data loss, or a mandated contract โ€” it is a Material finding and stays blocking until resolved. Writing down why you did an unsafe thing does not make it safe. --- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 13 ++++++++----- plugins/fx-dev/skills/dev/SKILL.md | 4 +++- .../fx-dev/skills/dev/references/scope-contract.md | 14 +++++++++++++- plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 79b3334..c8d02f2 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -100,7 +100,7 @@ Use `cr review --agent --base main` to scope to the branch's diff against `main` Treat findings like self-review feedback: -- **Triage by the materiality bar first** (`fx-dev/skills/dev/references/scope-contract.md`). Material and substantive findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. +- **Triage in the contract's order โ€” scope, then contract, then materiality** (`fx-dev/skills/dev/references/scope-contract.md`). An out-of-scope finding is deferred however material it looks, per the triage rules above; project rules and security/privacy invariants block regardless of the bar; only what remains is ranked. Material and substantive findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the project doesn't care about. - **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the tree; when it does not hold, reject the finding with the evidence rather than changing working code to satisfy a misreading. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 8edba51..d0a3819 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -253,11 +253,14 @@ than waiting it out. ### Step 2: Resolve every actionable finding -- **Triage by the materiality bar first** (see - `fx-dev/skills/dev/references/scope-contract.md`). Material and substantive - findings are fixed. Immaterial ones โ€” wording, formatting, a count nothing keys - on, an entry missing from a list the artifact declares non-exhaustive โ€” are - collected into one closing note and MUST NOT drive another iteration. +- **Triage in the contract's order โ€” scope, then contract, then materiality** + (see `fx-dev/skills/dev/references/scope-contract.md`). Scope first: an + out-of-scope finding is deferred however material it looks (Step 3.5). Then + project rules and security/privacy invariants, which block regardless of the + bar. Only what remains is ranked: material and substantive findings are fixed; + immaterial ones โ€” wording, formatting, a count nothing keys on, an entry + missing from a list the artifact declares non-exhaustive โ€” go into one closing + note and MUST NOT drive another iteration. - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the project doesn't care about. There are no PR threads to resolve here (this is diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 41d2ab5..14b37f7 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -316,7 +316,9 @@ Fix only findings classified `required-by-contract` or `regression-caused-by-cha Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeated or reworded findings against the ledger; they do not start a new cycle. When `/dev` invokes reviewer subskills, this contract classification and bounded stopping policy takes precedence over generic instructions to resolve every actionable finding or rerun until clean. -**A finding must clear the materiality bar before it can be classified at all** (`references/scope-contract.md`). An observation that would change nothing if it shipped uncorrected is neither `required-by-contract` nor `regression-caused-by-change` โ€” it is recorded in the ledger as immaterial and never triggers a rerun. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. +**Findings a reviewer originates from its own judgment must clear the materiality bar before they can be classified** (`references/scope-contract.md`). An observation that would change nothing if it shipped uncorrected is neither `required-by-contract` nor `regression-caused-by-change` โ€” it is recorded in the ledger as immaterial and never triggers a rerun. + +**The bar does NOT apply to violations of a mandatory rule.** Anything Step 2.5 defines as `required-by-contract` โ€” project, security, privacy, test and merge rules the project wrote down โ€” is blocking by virtue of being a rule, whatever its direct behavioural impact. The contract filter runs before the bar, exactly as `references/scope-contract.md` ยง Three filters specifies, so the coordinator can never demote a violation the reviewer correctly marked blocking. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. #### PR-Ready Stopping Condition diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index e6a6327..8db2f98 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -76,6 +76,16 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i This is not a licence to ignore problems. It is a ranking rule: report what clears the bar, and put what does not in one closing note rather than in the blocking list. +### Three filters, in this order + +Materiality is the **last** of three, and applying it out of order produces the wrong verdict: + +1. **Scope** โ€” is this finding ours at all? An out-of-scope finding is deferred with the exclusion that covers it, **however material it looks in isolation**. A valid improvement to work this change deliberately did not do is still out of scope. Materiality never promotes something back into scope. +2. **Contract** โ€” is it a violation of a project rule, security or privacy invariant, or another mandatory requirement the project wrote down? Those are **blocking by virtue of being rules**, and the bar does not filter them. The project already decided they matter; that decision is not a reviewer's to re-make per finding. +3. **Materiality** โ€” for everything left, which is findings a reviewer originates from its own judgment: does acting on it change anything? + +A finding that fails filter 1 is deferred. One that passes filter 2 is blocking. Only what reaches filter 3 is ranked by the bar below. + ### The bar | Tier | Examples | Treatment | @@ -90,7 +100,9 @@ When unsure which tier something is, ask: *if this shipped uncorrected, what bre **A missing entry in a list the artifact declares non-exhaustive.** If a document says "for example" or "this list is illustrative; the rule is authoritative", then supplying entry N+1 does not improve it โ€” the rule already covers N+1. Assess whether the *rule* is correct and sufficient. Reporting further missing entries against a declared-open list is the single most common way a review loop fails to terminate, because the supply of such entries never runs out. -**A decision already made, recorded, and reasoned.** Once an artifact states a decision with its rationale โ€” including a rationale that says "we do not know, and here is how we will find out" โ€” re-arguing it is not a review finding. If you believe the decision is *wrong*, say so once, plainly, as a single escalation to the human; do not re-raise it on the next pass in different words. A decision re-litigated across passes is a signal that it needs a person, not another round. +**A decision already made, recorded, and reasoned โ€” where the disagreement is about preference.** Once an artifact states a decision with its rationale, including a rationale that says "we do not know, and here is how we will find out", re-arguing it is not a review finding. Say so once as a single escalation; do not re-raise it on the next pass in different words. A preference re-litigated across passes needs a person, not another round. + +**But a recorded rationale does not make a decision safe.** If the decision *itself* is the defect โ€” it leaks a credential, loses data, violates a security or privacy invariant, or contradicts a contract the project mandates โ€” it is a **Material finding and stays blocking until resolved**, however carefully it is reasoned. Writing down why you did an unsafe thing does not make it safe. The distinction is whether you disagree with the choice or the choice is wrong: the first is an escalation, the second is a finding. **An artifact that admits a limit.** "This is verified by X at implementation time", "this is an open question gated on Y" โ€” those are dispositions, not gaps. Check the gate is real and sequenced before the thing that depends on it; do not report the limit itself. diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 9d9285e..d5e2059 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -21,7 +21,7 @@ Security, data-loss, and correctness problems **inside** the change are always i ## Also triage by materiality -Scope decides whether a finding is *ours*. Materiality decides whether it is *worth a fix* โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. +Scope decides whether a finding is *ours*. Materiality decides whether it is *worth a fix* โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`, which also fixes the order: **scope, then contract, then materiality**. An out-of-scope finding is deferred however material it looks, and a project-rule or security violation blocks regardless of the bar. Materiality ranks only what survives both. An in-scope finding that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is **resolved by replying with that reasoning**, not by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. From c502a0ad06b2307aa2db3124694cd86e213e62e4 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:01:58 +0000 Subject: [PATCH 04/48] fix(fx-dev): narrow the remaining fix-everything directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 3 found three, all the same shape: the new convergence rules landed in the middle of each skill while the top-level summary and the final gate still said "fix everything it flags" and "re-run until clean". An agent reading either could keep editing and rerunning nitpicks, which is the exact churn this change exists to stop. coderabbit-review's local-first summary and codex-review's introduction now scope fixes and reruns to material or substantive findings, and both PR gates read "converged" rather than "clean", with immaterial observations explicitly not holding the PR. copilot-review's Step 5 said repeat until zero new threads. Since resolving an immaterial thread by reply creates no commit, the head does not move and no further review is owed โ€” chasing a zero-thread pass would spend a wait cycle to change nothing. Convergence there is now no new material or substantive findings, every thread resolved, and the suppressed block triaged, all on a reviewed head, with the note that only a push restarts the loop. --- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 4 ++-- plugins/fx-dev/skills/codex-review/SKILL.md | 15 ++++++++++----- plugins/fx-dev/skills/copilot-review/SKILL.md | 4 +++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index c8d02f2..af14056 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -13,8 +13,8 @@ CodeRabbit reviews code with AI. The **primary** way to use it is **locally, via Catch CodeRabbit's feedback **before** a PR exists, using the `cr` CLI on your local changes: -- Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix everything it flags, and re-run until clean. -- Open the PR after the local review is clean **or correctly degraded as `skipped (rate-limited)`**. Resolve all actionable findings already received before proceeding. +- Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix every **material or substantive** finding, and re-run until a pass produces none. Immaterial observations get one closing note and do not buy another run โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. +- Open the PR once the local review has **converged** โ€” no material or substantive findings โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve all actionable findings already received before proceeding. - A clean local review does NOT remove the merge gates โ€” but it usually means CodeRabbit's PR-level review (when the GitHub App is configured) lands clean on the first pass, and often there is nothing left to resolve on the PR at all. ## The `cr` CLI diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index d0a3819..e173123 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -7,7 +7,10 @@ description: Run OpenAI Codex's AI code review LOCALLY via the `codex` CLI BEFOR This skill runs OpenAI Codex's AI code review **locally, one-shot**, against the current branch. Run it as part of pre-PR self-review โ€” after `coderabbit-review`, -before opening the PR โ€” fix everything it finds, and only then open the PR. +before opening the PR โ€” fix every **material or substantive** finding, and open +the PR once a pass produces none. Immaterial observations get one closing note +and do not hold the PR: see the materiality bar in +`fx-dev/skills/dev/references/scope-contract.md`. It complements (does not replace) `coderabbit-review`: CodeRabbit and Codex are independent reviewers and each catches issues the other misses. @@ -335,11 +338,13 @@ A run that produces **zero** out-of-scope findings is the signal the brief was well built. Persistent out-of-scope noise means the brief is too thin โ€” tighten it before the next iteration rather than filtering by hand again. -### Step 4: Open the PR only when clean +### Step 4: Open the PR once it has converged -A clean Codex review (alongside a clean CodeRabbit review) is the gate to PR -creation in the SDLC (`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with -unresolved actionable Codex findings. +A **converged** Codex review โ€” a pass with no material or substantive findings โ€” +alongside a converged CodeRabbit review is the gate to PR creation in the SDLC +(`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with unresolved material or +substantive Codex findings. Outstanding **immaterial** observations do not hold +the PR; carry them into its description as a closing note. ## When to Use This Skill diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index bcd538f..bb52d2b 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -296,7 +296,9 @@ on its own never passes it (**D4**). Resolving feedback usually means pushing commits. Those commits are **unreviewed**, and Copilot will not look at them by itself. -If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Repeat until a pass produces zero new threads *and* an empty-or-triaged suppressed block *on a reviewed head*. Cap at 4 iterations and escalate to the user if it has not settled. +If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 4 iterations and escalate to the user if it has not settled. + +**Convergence is: no new material or substantive findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only material and substantive findings should produce one. ```bash # The gate is only passed when the newest Copilot review covers the current head. From 8a3b490d5c26029ead7b2d106266fb3469a69240 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:14:23 +0000 Subject: [PATCH 05/48] fix(fx-dev): converge Mode 1's success criteria on materiality too Copilot found a leftover Codex's fourth pass had declared consistent: coderabbit-review's Mode 1 success criteria still required `cr review --agent` to report no ACTIONABLE findings, contradicting the convergence definition added a few sections above it and reintroducing the rerun-until-silent loop by the back door. Mode 1 now converges on no material or substantive findings, with remaining immaterial observations carried as a closing note. --- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index af14056..1fd7d61 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -208,8 +208,8 @@ Never call the Agent tool from inside a sub-agent context. ## Success Criteria **Mode 1 (local, primary):** -- โœ… `cr review --agent` reports no actionable findings after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)` -- โœ… All actionable findings received before any limit are resolved and committed +- โœ… `cr review --agent` reports **no material or substantive findings** after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)`. Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar +- โœ… All material and substantive findings received before any limit are resolved and committed - โœ… No cooldown waits or retries remain when the rate-limit exception applies **Mode 2 (PR-level, fallback / optional merge gate):** From 4660771b18020d7a55873b2f1f4384a2b140f7e8 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:18:23 +0000 Subject: [PATCH 06/48] fix(fx-dev): say which ledger classes the materiality bar gates Copilot's suppressed-comments block flagged a real ambiguity: "must clear the bar before they can be classified" reads as though immaterial findings are kept out of the ledger entirely, which the very next sentence contradicts by recording them there. The bar gates the two BLOCKING classes -- required-by-contract and regression-caused-by-change -- and nothing else. Everything is still recorded; immaterial findings are classified immaterial rather than excluded. Reworded to say so, which also fixes the awkward phrasing. --- plugins/fx-dev/skills/dev/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 14b37f7..03dc4a9 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -316,7 +316,7 @@ Fix only findings classified `required-by-contract` or `regression-caused-by-cha Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeated or reworded findings against the ledger; they do not start a new cycle. When `/dev` invokes reviewer subskills, this contract classification and bounded stopping policy takes precedence over generic instructions to resolve every actionable finding or rerun until clean. -**Findings a reviewer originates from its own judgment must clear the materiality bar before they can be classified** (`references/scope-contract.md`). An observation that would change nothing if it shipped uncorrected is neither `required-by-contract` nor `regression-caused-by-change` โ€” it is recorded in the ledger as immaterial and never triggers a rerun. +**A finding that a reviewer raised on its own judgment must clear the materiality bar before it can enter either BLOCKING class** โ€” `required-by-contract` or `regression-caused-by-change` (`references/scope-contract.md`). The bar gates those two classes only; it does not keep anything out of the ledger. An observation that would change nothing if it shipped uncorrected is still recorded, classified immaterial, and never triggers a rerun. **The bar does NOT apply to violations of a mandatory rule.** Anything Step 2.5 defines as `required-by-contract` โ€” project, security, privacy, test and merge rules the project wrote down โ€” is blocking by virtue of being a rule, whatever its direct behavioural impact. The contract filter runs before the bar, exactly as `references/scope-contract.md` ยง Three filters specifies, so the coordinator can never demote a violation the reviewer correctly marked blocking. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. From 92a47259a7f2ffb38e6e8eec36a315169acf8d65 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:21:26 +0000 Subject: [PATCH 07/48] fix(fx-dev): restore the suppressed-comments gate and drop a stale "actionable" Two suppressed Copilot findings, both real. Rewriting resolve-pr-feedback's step 6 around threads dropped the suppressed-comments condition that the previous "zero new feedback" wording implicitly carried. Since those findings create no thread, the rewritten rule could converge with real findings outstanding -- contradicting the skill's own success criterion 1 and the D4 warning it is built around. Step 6 now requires the suppressed block for the reviewed head to be empty or fully triaged, alongside no new material/substantive findings and all threads resolved. coderabbit-review's local-first bullet still said "resolve all actionable findings" in the same sentence that defined convergence as no material or substantive findings, which reintroduces the fix-everything reading it was meant to remove. --- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 2 +- plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 1fd7d61..4c1ae82 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -14,7 +14,7 @@ CodeRabbit reviews code with AI. The **primary** way to use it is **locally, via Catch CodeRabbit's feedback **before** a PR exists, using the `cr` CLI on your local changes: - Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix every **material or substantive** finding, and re-run until a pass produces none. Immaterial observations get one closing note and do not buy another run โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. -- Open the PR once the local review has **converged** โ€” no material or substantive findings โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve all actionable findings already received before proceeding. +- Open the PR once the local review has **converged** โ€” no material or substantive findings โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve the material and substantive findings already received before proceeding; immaterial ones travel as a closing note. - A clean local review does NOT remove the merge gates โ€” but it usually means CodeRabbit's PR-level review (when the GitHub App is configured) lands clean on the first pass, and often there is nothing left to resolve on the PR at all. ## The `cr` CLI diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index d5e2059..bd036af 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`) **and every automated thread on that head is resolved**. Immaterial threads resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. +6. Stop when a pass produces **no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. From ee4de764df1f15d3ec97d1238376f5c3fcb8b100 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:24:53 +0000 Subject: [PATCH 08/48] fix(fx-dev): scope the bar to judgment-originated findings, align terminology Four suppressed Copilot findings. Two substantive, two terminology; all four ride in one push because the bar governs whether an observation buys another review CYCLE, not whether you fix it while already editing the file. The dev paragraphs read as a contradiction: the first gated entry into required-by-contract on the bar, the second said the bar does not apply to mandatory-rule violations, which are classified required-by-contract. Both are now framed by origin rather than by class -- the bar applies to what a reviewer raised on its own reading; violations of a written rule are blocking regardless. The materiality headline in the shared contract stated the test universally, then the very next section carved out mandatory rules, inviting a reader to apply the first sentence too broadly. It is now scoped to what survives the scope and contract filters. resolve-pr-feedback and copilot-review called below-the-bar items "findings" where the contract defines them as observations. The loop logic keys on whether something is a new finding, so the two words should not disagree. --- plugins/fx-dev/skills/copilot-review/SKILL.md | 7 ++++--- plugins/fx-dev/skills/dev/SKILL.md | 4 ++-- plugins/fx-dev/skills/dev/references/scope-contract.md | 2 +- plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index bb52d2b..895f02d 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -100,9 +100,10 @@ not handed it. on it. Triage by **materiality** as well as scope โ€” see the materiality bar in the same -reference. An in-scope finding that would change nothing if it shipped -uncorrected is **resolved by replying with that reasoning**, not by editing. The -gate is zero *unresolved* Copilot threads, not zero observations acted on. +reference. An in-scope item that would change nothing if it shipped uncorrected +is an **observation, not a finding**, and is **resolved by replying with that +reasoning** rather than by editing. The gate is zero *unresolved* Copilot +threads, not zero observations acted on. This matters more here than anywhere else in the loop: **every push re-opens the gate.** Copilot must then re-review the new head (Step 5), so editing for an diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 03dc4a9..18e6900 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -316,9 +316,9 @@ Fix only findings classified `required-by-contract` or `regression-caused-by-cha Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeated or reworded findings against the ledger; they do not start a new cycle. When `/dev` invokes reviewer subskills, this contract classification and bounded stopping policy takes precedence over generic instructions to resolve every actionable finding or rerun until clean. -**A finding that a reviewer raised on its own judgment must clear the materiality bar before it can enter either BLOCKING class** โ€” `required-by-contract` or `regression-caused-by-change` (`references/scope-contract.md`). The bar gates those two classes only; it does not keep anything out of the ledger. An observation that would change nothing if it shipped uncorrected is still recorded, classified immaterial, and never triggers a rerun. +**The materiality bar applies to judgment-originated findings only** โ€” things a reviewer raised on its own reading, rather than violations of a rule the project wrote down (`references/scope-contract.md`). One of those must clear the bar before it can be treated as blocking. An observation that would change nothing if it shipped uncorrected is still recorded in the ledger, classified immaterial, and never triggers a rerun. -**The bar does NOT apply to violations of a mandatory rule.** Anything Step 2.5 defines as `required-by-contract` โ€” project, security, privacy, test and merge rules the project wrote down โ€” is blocking by virtue of being a rule, whatever its direct behavioural impact. The contract filter runs before the bar, exactly as `references/scope-contract.md` ยง Three filters specifies, so the coordinator can never demote a violation the reviewer correctly marked blocking. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. +**Rule violations are blocking regardless of materiality.** Anything Step 2.5 defines as `required-by-contract` โ€” project, security, privacy, test and merge rules the project wrote down โ€” is blocking by virtue of being a rule, whatever its direct behavioural impact. The contract filter runs before the bar, exactly as `references/scope-contract.md` ยง Three filters specifies, so the coordinator can never demote a violation the reviewer correctly marked blocking. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. #### PR-Ready Stopping Condition diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 8db2f98..f017942 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -72,7 +72,7 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i ## The materiality bar -**A finding is worth reporting when acting on it would change what the artifact does, or change how a competent reader acts on it. Everything else is an observation, not a finding.** +**Of the findings a reviewer originates from its own judgment โ€” that is, what survives the scope and contract filters below โ€” one is worth reporting when acting on it would change what the artifact does, or change how a competent reader acts on it. Everything else is an observation, not a finding.** This is not a licence to ignore problems. It is a ranking rule: report what clears the bar, and put what does not in one closing note rather than in the blocking list. diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index bd036af..fe5b4ef 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -23,7 +23,7 @@ Security, data-loss, and correctness problems **inside** the change are always i Scope decides whether a finding is *ours*. Materiality decides whether it is *worth a fix* โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`, which also fixes the order: **scope, then contract, then materiality**. An out-of-scope finding is deferred however material it looks, and a project-rule or security violation blocks regardless of the bar. Materiality ranks only what survives both. -An in-scope finding that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is **resolved by replying with that reasoning**, not by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. +An in-scope item that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is an **observation, not a finding**, and is **resolved by replying with that reasoning** rather than by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for material and substantive findings; reply-and-resolve the rest in the same cycle. From d95dd5b5286cd61405212b443354d6ba7f3d1831 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:28:37 +0000 Subject: [PATCH 09/48] fix(fx-dev): finish the actionable-to-converged sweep, add privacy Two suppressed Copilot findings, plus a proactive sweep for the same two classes across every file this change touches, so they stop arriving one per review cycle. coderabbit-review's Mode 2 gate still said "do not open the PR with known unresolved actionable findings", the last of the fix-until-silent wordings. A grep for "unresolved actionable", "until clean" and "reports no actionable" across all five review skills now returns nothing. resolve-pr-feedback's triage-order paragraph listed "project rule or security violation" and omitted privacy, which the contract filter treats as blocking, so a resolver could have triaged a privacy issue as immaterial. The other two paragraphs of the same shape already said security/privacy; this one now matches. --- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 2 +- plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 4c1ae82..9a2f50c 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -119,7 +119,7 @@ Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass p ### Step 4: Open the PR when clean or correctly degraded -A clean local CodeRabbit review is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once all findings already received are addressed. Do not open the PR with known unresolved actionable findings. +A **converged** local CodeRabbit review โ€” no material or substantive findings โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once the material and substantive findings already received are addressed. Do not open the PR with known unresolved material or substantive findings; immaterial observations travel as a closing note in the PR description. --- diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index fe5b4ef..6c8bb46 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -21,7 +21,7 @@ Security, data-loss, and correctness problems **inside** the change are always i ## Also triage by materiality -Scope decides whether a finding is *ours*. Materiality decides whether it is *worth a fix* โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`, which also fixes the order: **scope, then contract, then materiality**. An out-of-scope finding is deferred however material it looks, and a project-rule or security violation blocks regardless of the bar. Materiality ranks only what survives both. +Scope decides whether a finding is *ours*. Materiality decides whether it is *worth a fix* โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`, which also fixes the order: **scope, then contract, then materiality**. An out-of-scope finding is deferred however material it looks, and a violation of a project rule or a security or privacy invariant blocks regardless of the bar. Materiality ranks only what survives both. An in-scope item that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is an **observation, not a finding**, and is **resolved by replying with that reasoning** rather than by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. From 8d159fdab824f6213ae5d45eb6b89722157c61d8 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:31:24 +0000 Subject: [PATCH 10/48] fix(fx-dev): rephrase the materiality headline The previous round's rewrite scoped the bar correctly but introduced a grammatical ambiguity in the process: "...one is worth reporting..." reads as "only one finding is worth reporting". That is the headline sentence of the whole rule, so a misreading there propagates. Split into two sentences: what the bar applies to, then when such a finding clears it. --- plugins/fx-dev/skills/dev/references/scope-contract.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index f017942..dd578ea 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -72,7 +72,7 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i ## The materiality bar -**Of the findings a reviewer originates from its own judgment โ€” that is, what survives the scope and contract filters below โ€” one is worth reporting when acting on it would change what the artifact does, or change how a competent reader acts on it. Everything else is an observation, not a finding.** +**This bar applies to findings a reviewer originates from its own judgment โ€” what survives the scope and contract filters below. Such a finding is worth reporting when acting on it would change what the artifact does, or change how a competent reader acts on it. Everything else is an observation, not a finding.** This is not a licence to ignore problems. It is a ranking rule: report what clears the bar, and put what does not in one closing note rather than in the blocking list. From 9dce5918627ebb467236896f7d532d52c83ee862 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Fri, 14 Aug 2026 23:34:25 +0000 Subject: [PATCH 11/48] fix(fx-dev): disambiguate the suppressed-comments cross-reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot read "(step 3 ...)" in the convergence condition as this loop's step 3, which is the unresolved-threads re-query, and concluded the suppressed-comments triage was unreferenced. Its premise is wrong -- the document's ยง3 does carry that instruction, including the commit-scoped fetch and the triage rule. But the reference is genuinely ambiguous, because the loop has its own step 3 a few lines above, so both readings are available and only one is right. Now points at the section by name and says which reading is meant. --- plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 6c8bb46..e326eb3 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. +6. Stop when a pass produces **no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. From bb41aaf0d5cd5149fb30dd36152a0346489c6f94 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 05:14:32 +0000 Subject: [PATCH 12/48] fix(fx-dev): close four materiality-bar gaps codex found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex review pass 1 on this branch, run with the Scope Brief, returned four in-scope findings and no out-of-scope noise. All four premises were verified against the tree before fixing. P1, the only material one: the "a recorded decision is settled" shorthand was copied into codex-review's convergence prompt, dev's ledger rules, and pr-reviewer's non-findings list without the two qualifiers the canonical contract carries โ€” "where the disagreement is about preference", and the carve-out that a recorded rationale never makes an unsafe decision safe. As written, a reviewer could have read a documented rationale for leaking a credential as settled ground. All three copies now carry both qualifiers. The other three were substantive ambiguities that let an agent act two ways: - dev told the coordinator to record an observation "classified immaterial", but the ledger schema admits exactly three classifications and immaterial is not one. Materiality is now an independent field alongside classification, and the schema records it. - pr-reviewer required substantive findings to be reported individually and immaterial ones as one closing note, while its output template offered only "Critical issues" and "Nice improvements" โ€” no home for either. The template now has all three tiers. - The bar's Material row listed "a stated fact that is false" while the Immaterial row listed "a count off by one where nothing keys on the count", and a wrong step count satisfies both. Reader impact now breaks the tie, with worked examples on each side. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 7 +++++-- plugins/fx-dev/skills/dev/SKILL.md | 6 +++--- .../skills/dev/references/scope-contract.md | 4 +++- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 21 ++++++++++++++----- 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 5c54262..75482b0 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.22.0", + "version": "0.22.1", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 2bcbc1f..fab58b2 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": "3.9.0", + "version": "3.9.1", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index e173123..823bf4b 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -311,8 +311,11 @@ Where this artifact declares a list illustrative and a rule authoritative, assess the RULE. A further missing list entry is not a finding. Where it records a decision with its rationale โ€” including "unknown, gated on -X" โ€” that is settled. If you believe it is wrong, say so once as an escalation; -do not re-argue it. +X" โ€” and your disagreement is about preference, that is settled: say so once as +an escalation, and do not re-argue it. This does NOT cover a decision that is +itself the defect. If the decision leaks a credential, an internal URL, or a +private identifier, loses data, or violates a security or privacy invariant, +report it as a blocking finding however carefully it is reasoned. If the artifact is internally consistent and matches the tree, say so plainly. ``` diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 18e6900..cb67169 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -184,7 +184,7 @@ full review cycle to filter by hand. **Then freeze the implementation contract.** If the task is sourced from, names, or discovers a relevant `docs/changes/*.md` file, read it and the spec sections it links. Confirm implementation approval from the conversation or the change's recorded workflow state; if approval is unclear, STOP and ask the user. Record the contract path and approval evidence in the working brief. The change document, its linked specs, and all mandatory project rules form the implementation contract: the plan and coder prompt MUST map work to that contract and MUST NOT infer adjacent product or architecture work. -The coordinator owns one in-memory finding ledger for the run; reviewer sub-agents return findings to the coordinator and MUST NOT mutate the ledger concurrently. Give every finding a stable fingerprint (`category + file + line/range + normalized claim`) and record its source, first-seen revision, classification, disposition, and verification evidence. Classify each finding exactly once as: +The coordinator owns one in-memory finding ledger for the run; reviewer sub-agents return findings to the coordinator and MUST NOT mutate the ledger concurrently. Give every finding a stable fingerprint (`category + file + line/range + normalized claim`) and record its source, first-seen revision, classification, materiality tier, disposition, and verification evidence. Classification and materiality are independent fields โ€” see Step 4.5 for how the tier is assigned. Classify each finding exactly once as: - **required-by-contract** โ€” Necessary to satisfy the change document, its linked specs, or any mandatory project, security, privacy, test, or merge rule. - **regression-caused-by-change** โ€” A demonstrable correctness, security, privacy, or data-loss regression caused by this branch anywhere within its behavioral impact, including downstream consumers or integrations. @@ -316,9 +316,9 @@ Fix only findings classified `required-by-contract` or `regression-caused-by-cha Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeated or reworded findings against the ledger; they do not start a new cycle. When `/dev` invokes reviewer subskills, this contract classification and bounded stopping policy takes precedence over generic instructions to resolve every actionable finding or rerun until clean. -**The materiality bar applies to judgment-originated findings only** โ€” things a reviewer raised on its own reading, rather than violations of a rule the project wrote down (`references/scope-contract.md`). One of those must clear the bar before it can be treated as blocking. An observation that would change nothing if it shipped uncorrected is still recorded in the ledger, classified immaterial, and never triggers a rerun. +**The materiality bar applies to judgment-originated findings only** โ€” things a reviewer raised on its own reading, rather than violations of a rule the project wrote down (`references/scope-contract.md`). One of those must clear the bar before it can be treated as blocking. Materiality is a **separate ledger field**, not a fourth classification โ€” every entry still carries exactly one of the three classifications above, plus a materiality tier. An observation that would change nothing if it shipped uncorrected is recorded as `follow-up/out-of-scope` with materiality `immaterial`, and never triggers a rerun. -**Rule violations are blocking regardless of materiality.** Anything Step 2.5 defines as `required-by-contract` โ€” project, security, privacy, test and merge rules the project wrote down โ€” is blocking by virtue of being a rule, whatever its direct behavioural impact. The contract filter runs before the bar, exactly as `references/scope-contract.md` ยง Three filters specifies, so the coordinator can never demote a violation the reviewer correctly marked blocking. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale. The second, raised twice, is an escalation to the user rather than a third cycle. +**Rule violations are blocking regardless of materiality.** Anything Step 2.5 defines as `required-by-contract` โ€” project, security, privacy, test and merge rules the project wrote down โ€” is blocking by virtue of being a rule, whatever its direct behavioural impact. The contract filter runs before the bar, exactly as `references/scope-contract.md` ยง Three filters specifies, so the coordinator can never demote a violation the reviewer correctly marked blocking. Two shapes in particular are non-findings and MUST NOT enter the ledger as blocking: a missing entry in a list the artifact declares non-exhaustive, and a decision the artifact records with its rationale **where the disagreement is about preference**. The second, raised twice, is an escalation to the user rather than a third cycle โ€” but a recorded rationale never makes a decision safe: if the decision itself leaks a credential or private identifier, loses data, or violates a security or privacy invariant, it is `required-by-contract` and stays blocking. #### PR-Ready Stopping Condition diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index dd578ea..1f7cd02 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -90,12 +90,14 @@ A finding that fails filter 1 is deferred. One that passes filter 2 is blocking. | Tier | Examples | Treatment | |---|---|---| -| **Material** | Wrong behaviour, data loss, security, a build/CI/test that will fail, a contradiction that makes the artifact unimplementable, a stated fact that is false | Report individually. Blocks convergence. | +| **Material** | Wrong behaviour, data loss, security, a build/CI/test that will fail, a contradiction that makes the artifact unimplementable, a stated fact that is false **and that a reader would act on** | Report individually. Blocks convergence. | | **Substantive** | A genuine ambiguity a reader could act on two ways; a missing step that would be discovered late and cost a cycle | Report individually. Blocks convergence. | | **Immaterial** | Wording that is merely improvable; a count off by one where nothing keys on the count; a list entry missing from a list nothing enumerates exhaustively; formatting; a synonym that reads better | **One closing note, unnumbered, non-blocking.** Never a separate finding, never a reason for another pass. | When unsure which tier something is, ask: *if this shipped uncorrected, what breaks?* If the honest answer is "nothing, it is just not as good as it could be", it is immaterial. +**When two rows both seem to fit, that question decides it โ€” reader impact, not the category label.** A document that says "four steps" above five steps is both a false stated fact and a count nothing keys on; it is **immaterial**, because no reader acts on the number. The same document saying a command takes `--base` when it rejects `--base` is **material**, because a reader will run it and it will fail. The falsehood is not what makes a finding material; acting on the falsehood is. + ### Three things that are not findings **A missing entry in a list the artifact declares non-exhaustive.** If a document says "for example" or "this list is illustrative; the rule is authoritative", then supplying entry N+1 does not improve it โ€” the rule already covers N+1. Assess whether the *rule* is correct and sufficient. Reporting further missing entries against a declared-open list is the single most common way a review loop fails to terminate, because the supply of such entries never runs out. diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index c58eed4..299bf3e 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -46,7 +46,8 @@ whether it is worth their time** โ€” see the materiality bar in the same referen - **Report material and substantive findings individually.** Wrong behaviour, data loss, security, a build or test that will fail, a contradiction that makes - the change unimplementable, a stated fact that is false, or a genuine ambiguity + the change unimplementable, a stated fact that is false and that a reader would + act on, or a genuine ambiguity a reader could act on two ways. - **Collect everything else into one closing note**, unnumbered and explicitly non-blocking. Wording, formatting, naming preference, a count nothing keys on. @@ -59,8 +60,11 @@ noise the author learns to skim: - a missing entry in a list the change itself declares illustrative โ€” assess the rule instead, since the supply of such entries never runs out; -- a decision the change records with its rationale โ€” if you think it is wrong, - say so once as an escalation rather than re-arguing it; +- a decision the change records with its rationale, **where your disagreement is + about preference** โ€” say so once as an escalation rather than re-arguing it. + This never applies when the decision is itself the defect: a recorded rationale + for leaking a credential or private identifier, losing data, or violating a + security or privacy invariant is a blocking finding, however well reasoned; - a limit the change admits and gates ("verified at implementation time", "open question gated on X") โ€” check the gate is real and sequenced, and move on. @@ -133,10 +137,17 @@ For projects with vendor submodules (e.g., `vendor/` directory): **Ready**: YES/NO ### Blocking -- [Critical issues only] +- [Material findings: wrong behaviour, data loss, security, privacy, a build or + test that will fail, a false stated fact, a project-rule violation] +- [Substantive findings: a genuine ambiguity a reader could act on two ways, a + missing step that would be discovered late] ### Suggestions -- [Nice improvements] +- [Optional improvements that are in scope but not blocking] + +### Closing note (non-blocking) +- [One unnumbered paragraph for everything immaterial โ€” wording, formatting, + naming preference, counts nothing keys on. Never itemized as findings.] ### Next - [Clear actions] From 3f2de5c116f1513c2a03478d1429c523be24f456 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 05:28:27 +0000 Subject: [PATCH 13/48] fix(fx-dev): make contract blockers count toward convergence Codex pass 2 returned four in-scope findings, two of which were regressions from pass 1's own fixes. All four premises were verified before fixing. The two genuinely new ones are the same defect at two altitudes. Filter 2 declares contract violations blocking and explicitly leaves them unranked, but every convergence condition was written purely in terms of the two blocking tiers. Read literally, a pass carrying an unresolved security or privacy rule violation and no judgment-originated findings was "converged". The canonical rule now names contract blockers as a first-class stopping condition, and the eight places that restate the condition across codex-review, coderabbit-review, copilot-review and resolve-pr-feedback were swept in the same commit rather than left to arrive one per review cycle. The runtime prompt codex-review injects into `codex review` had the matching gap: its reporting list named only Material cases, so a two-way ambiguity or a late-discovered missing step could be suppressed and the pass would falsely converge. It also said "state something false" flatly, contradicting the reader-impact qualifier added in the previous commit. Both now mirror the canonical bar. The two regressions were in pr-reviewer's output template. Its Blocking section had dropped the reader-impact qualifier, so a harmless off-by-one count would land under Blocking against the canonical worked example; and it kept a Suggestions section beside the new closing note, giving in-scope non-blocking items two homes when the bar defines exactly one. Suggestions is folded into the closing note and the qualifier is restored. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 6 +++--- plugins/fx-dev/skills/codex-review/SKILL.md | 15 +++++++++------ plugins/fx-dev/skills/copilot-review/SKILL.md | 2 +- .../skills/dev/references/scope-contract.md | 7 ++++--- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 15 ++++++++------- .../fx-dev/skills/resolve-pr-feedback/SKILL.md | 4 ++-- 8 files changed, 29 insertions(+), 24 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 75482b0..3928100 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.22.1", + "version": "0.22.2", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index fab58b2..cdf8451 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": "3.9.1", + "version": "3.9.2", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 9a2f50c..9a510a6 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -108,7 +108,7 @@ Treat findings like self-review feedback: ### Step 3: Re-run until it converges (REQUIRED) -Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass produces no material or substantive findings.** +Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass produces no contract blockers and no material or substantive findings.** **Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. @@ -119,7 +119,7 @@ Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass p ### Step 4: Open the PR when clean or correctly degraded -A **converged** local CodeRabbit review โ€” no material or substantive findings โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once the material and substantive findings already received are addressed. Do not open the PR with known unresolved material or substantive findings; immaterial observations travel as a closing note in the PR description. +A **converged** local CodeRabbit review โ€” no contract blockers and no material or substantive findings โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once the material and substantive findings already received are addressed. Do not open the PR with known unresolved material or substantive findings; immaterial observations travel as a closing note in the PR description. --- @@ -208,7 +208,7 @@ Never call the Agent tool from inside a sub-agent context. ## Success Criteria **Mode 1 (local, primary):** -- โœ… `cr review --agent` reports **no material or substantive findings** after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)`. Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar +- โœ… `cr review --agent` reports **no contract blockers and no material or substantive findings** after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)`. Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar - โœ… All material and substantive findings received before any limit are resolved and committed - โœ… No cooldown waits or retries remain when the rate-limit exception applies diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 823bf4b..e071054 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -285,7 +285,7 @@ than waiting it out. Run the review again after fixes, **carrying the same Scope Brief prompt plus anything newly established**. Note the iteration number in the prompt and add facts verified since the last pass, so Codex does not relitigate settled ground. -**Repeat Steps 1 โ†’ 2 until a pass produces no material or substantive findings** +**Repeat Steps 1 โ†’ 2 until a pass produces no contract blockers and no material or substantive findings** โ€” per the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. **Converged does NOT mean zero output.** Codex will keep producing immaterial @@ -302,10 +302,13 @@ CONVERGENCE PASS . Prior passes found issues; all fixed except . Do not re-report them. Report findings that would change behaviour, break a build or test, make the -artifact unimplementable, state something false, or expose a security, privacy, -or data-loss problem โ€” including a leaked credential, internal URL, or private -identifier in documentation or examples. Wording, formatting, and counts nothing -keys on: one closing note, not findings. +artifact unimplementable, or expose a security, privacy, or data-loss problem โ€” +including a leaked credential, internal URL, or private identifier in +documentation or examples. A false statement counts when a reader would act on +it; a wrong number nothing keys on does not. Also report a genuine ambiguity a +reader could act on two ways, and a missing step that would be discovered late +and cost a cycle โ€” those block too. Wording, formatting, and counts nothing keys +on: one closing note, not findings. Where this artifact declares a list illustrative and a rule authoritative, assess the RULE. A further missing list entry is not a finding. @@ -343,7 +346,7 @@ it before the next iteration rather than filtering by hand again. ### Step 4: Open the PR once it has converged -A **converged** Codex review โ€” a pass with no material or substantive findings โ€” +A **converged** Codex review โ€” a pass with no contract blockers and no material or substantive findings โ€” alongside a converged CodeRabbit review is the gate to PR creation in the SDLC (`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with unresolved material or substantive Codex findings. Outstanding **immaterial** observations do not hold diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index 895f02d..44ddbd2 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -299,7 +299,7 @@ Resolving feedback usually means pushing commits. Those commits are **unreviewed If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 4 iterations and escalate to the user if it has not settled. -**Convergence is: no new material or substantive findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only material and substantive findings should produce one. +**Convergence is: no contract blockers, no new material or substantive findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only material and substantive findings should produce one. ```bash # The gate is only passed when the newest Copilot review covers the current head. diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 1f7cd02..0416fa7 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -84,7 +84,7 @@ Materiality is the **last** of three, and applying it out of order produces the 2. **Contract** โ€” is it a violation of a project rule, security or privacy invariant, or another mandatory requirement the project wrote down? Those are **blocking by virtue of being rules**, and the bar does not filter them. The project already decided they matter; that decision is not a reviewer's to re-make per finding. 3. **Materiality** โ€” for everything left, which is findings a reviewer originates from its own judgment: does acting on it change anything? -A finding that fails filter 1 is deferred. One that passes filter 2 is blocking. Only what reaches filter 3 is ranked by the bar below. +A finding that fails filter 1 is deferred. One that passes filter 2 is a **contract blocker** โ€” blocking on its own terms, and never ranked by the bar. Only what reaches filter 3 is ranked below. Contract blockers are unranked but not unblocking: convergence requires zero of them as well as zero material and substantive findings. ### The bar @@ -110,12 +110,13 @@ When unsure which tier something is, ask: *if this shipped uncorrected, what bre ### Convergence -**A review has converged when a pass produces no material or substantive findings โ€” not when it produces zero output.** Zero is usually unreachable and waiting for it burns cycles on immaterial churn. +**A review has converged when a pass produces no unresolved contract blockers and no material or substantive findings โ€” not when it produces zero output.** Zero is usually unreachable and waiting for it burns cycles on immaterial churn. An outstanding contract blocker prevents convergence by itself: it is unranked precisely because the project already decided it blocks, so "no material or substantive findings" never clears it. Track findings per pass and watch the shape, not just the count: +- **Contract blockers outstanding** โ€” not converged, whatever the tiers did. - **Material or substantive findings still arriving, in new categories** โ€” keep going. The review is still working. -- **No material or substantive findings this pass** โ€” converged, whatever the immaterial count did. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. +- **No contract blockers and no material or substantive findings this pass** โ€” converged, whatever the immaterial count did. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. - **The same disagreement in successive passes** โ€” stop. That is a human decision, not a review outcome. Escalate it by name. Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 material. Stopping โ€” three immaterial wording items remain, listed below."* diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index 299bf3e..e3093d0 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -137,17 +137,18 @@ For projects with vendor submodules (e.g., `vendor/` directory): **Ready**: YES/NO ### Blocking -- [Material findings: wrong behaviour, data loss, security, privacy, a build or - test that will fail, a false stated fact, a project-rule violation] +- [Contract blockers: a project, security, or privacy rule violation โ€” blocking + by virtue of being a rule, never ranked by the bar] +- [Material findings: wrong behaviour, data loss, security, a build or test that + will fail, a stated fact that is false AND that a reader would act on] - [Substantive findings: a genuine ambiguity a reader could act on two ways, a missing step that would be discovered late] -### Suggestions -- [Optional improvements that are in scope but not blocking] - ### Closing note (non-blocking) -- [One unnumbered paragraph for everything immaterial โ€” wording, formatting, - naming preference, counts nothing keys on. Never itemized as findings.] +- [One unnumbered paragraph for everything that clears neither tier โ€” wording, + formatting, naming preference, counts nothing keys on, optional improvements. + There is no middle tier: if it is in scope and does not block, it belongs + here, collapsed into prose and never itemized as findings.] ### Next - [Clear actions] diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index e326eb3..8b8f2ed 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. +6. Stop when a pass produces **no contract blockers and no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. @@ -287,7 +287,7 @@ If unresolved threads remain, report which reviewers still have open feedback. 1. All unresolved automated review threads identified โ€” matched on the `copilot-pull-request-reviewer` login, **not** the bare `Copilot` โ€” **plus** any findings in the "Suppressed comments" block of every Copilot review **of the current head commit**, which produce no threads 2. Appropriate resolver skill(s) invoked (Copilot + CodeRabbit in parallel where applicable) -3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, produced no new material or substantive findings, and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence +3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, produced no contract blockers and no new material or substantive findings, and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence 4. CodeRabbit's check is in a terminal passing state (or absent if not configured) 5. Final verification confirms all threads resolved 6. Summary output provided From 04445e6bb47ff69c06a472ca5054799995282e82 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 17:33:54 +0000 Subject: [PATCH 14/48] feat(fx-dev): collapse the bar's stopping rules into one blocking term MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 3 returned 8 findings against pass 2's 4 โ€” a rising count, all of one class. The cause was pass 2's own fix: naming "contract blocker" as a third category turned every one of the 14 sites reading "material or substantive" into an incomplete enumeration, so each was a place a rule violation could slip through a convergence check. Fixing the instances would have reproduced the problem on the next category. scope-contract.md now defines BLOCKING once โ€” contract blocker, Material, or Substantive โ€” and states that skills MUST NOT re-enumerate the constituents in their own stopping rules. All 14 sites across codex-review, coderabbit-review, copilot-review, resolve-pr-feedback, pr-reviewer and dev now say "blocking" and point at the definition. Adding a fourth category later is one edit. That structurally closes six of pass 3's eight findings. The rest were fixed directly: - The prompt codex-review injects into `codex review` never asked for contract blockers at all, so a rule violation with no behavioural symptom could go unreported and the pass would falsely converge. It now enumerates all three blocking kinds, rule violations first. - The ledger required a materiality tier on every entry while the contract says contract blockers are never ranked. The tier is now explicitly `n/a` for them โ€” inventing one is what lets a rule violation be argued down. - The PR-ready stopping condition tested classification only, so a Substantive finding filed as follow-up/out-of-scope passed the gate. It now tests the blocking tier as well: classification decides who owns a finding, the tier decides whether it holds the gate. - A recorded-but-unsafe decision was labelled Material while filter 2 had already made it a contract blocker. Both block, so the disposition never differed; the text now says so instead of assigning two classifications. Also records the divergence signal itself in both the contract and codex-review: a pass returning more blocking findings than the last, all of one class, means the previous fix is generating them โ€” address the cause, and escalate if the cause is a design choice with two defensible answers. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 16 +++---- plugins/fx-dev/skills/codex-review/SKILL.md | 45 ++++++++++++------- plugins/fx-dev/skills/copilot-review/SKILL.md | 4 +- plugins/fx-dev/skills/dev/SKILL.md | 6 +-- .../skills/dev/references/scope-contract.md | 31 +++++++++---- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 2 +- .../skills/resolve-pr-feedback/SKILL.md | 6 +-- 9 files changed, 69 insertions(+), 45 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3928100..73366cb 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.22.2", + "version": "0.23.0", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index cdf8451..108785d 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": "3.9.2", + "version": "3.10.0", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 9a510a6..0e16f4a 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -13,8 +13,8 @@ CodeRabbit reviews code with AI. The **primary** way to use it is **locally, via Catch CodeRabbit's feedback **before** a PR exists, using the `cr` CLI on your local changes: -- Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix every **material or substantive** finding, and re-run until a pass produces none. Immaterial observations get one closing note and do not buy another run โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. -- Open the PR once the local review has **converged** โ€” no material or substantive findings โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve the material and substantive findings already received before proceeding; immaterial ones travel as a closing note. +- Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix every **blocking** finding, and re-run until a pass produces none. Immaterial observations get one closing note and do not buy another run โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. +- Open the PR once the local review has **converged** โ€” no blocking findings โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve the blocking findings already received before proceeding; immaterial ones travel as a closing note. - A clean local review does NOT remove the merge gates โ€” but it usually means CodeRabbit's PR-level review (when the GitHub App is configured) lands clean on the first pass, and often there is nothing left to resolve on the PR at all. ## The `cr` CLI @@ -100,7 +100,7 @@ Use `cr review --agent --base main` to scope to the branch's diff against `main` Treat findings like self-review feedback: -- **Triage in the contract's order โ€” scope, then contract, then materiality** (`fx-dev/skills/dev/references/scope-contract.md`). An out-of-scope finding is deferred however material it looks, per the triage rules above; project rules and security/privacy invariants block regardless of the bar; only what remains is ranked. Material and substantive findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. +- **Triage in the contract's order โ€” scope, then contract, then materiality** (`fx-dev/skills/dev/references/scope-contract.md`). An out-of-scope finding is deferred however material it looks, per the triage rules above; project rules and security/privacy invariants block regardless of the bar; only what remains is ranked. Blocking findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the project doesn't care about. - **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the tree; when it does not hold, reject the finding with the evidence rather than changing working code to satisfy a misreading. @@ -108,18 +108,18 @@ Treat findings like self-review feedback: ### Step 3: Re-run until it converges (REQUIRED) -Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass produces no contract blockers and no material or substantive findings.** +Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass produces no blocking findings.** **Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. - **Cap at 4 iterations.** If CodeRabbit keeps flagging the same design decision after 4 passes, that is a human call, not more code edits โ€” escalate it **by name** and stop. -- Watch the shape, and count only the tiers that block: material or substantive findings still arriving โ†’ keep going; a pass with none โ†’ converged, however many immaterial observations it produced; the same disagreement twice โ†’ escalate. +- Watch the shape, and count only what blocks: blocking findings still arriving โ†’ keep going; a pass with none โ†’ converged, however many immaterial observations it produced; the same disagreement twice โ†’ escalate. - **Rate-limit exception:** stop immediately on throttling; do not consume iterations waiting for cooldowns. - When you stop, report the per-pass trend and whether the last round's fixes were themselves reviewed. ### Step 4: Open the PR when clean or correctly degraded -A **converged** local CodeRabbit review โ€” no contract blockers and no material or substantive findings โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once the material and substantive findings already received are addressed. Do not open the PR with known unresolved material or substantive findings; immaterial observations travel as a closing note in the PR description. +A **converged** local CodeRabbit review โ€” no blocking findings โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once every blocking finding already received is resolved โ€” the degradation waives the *unrun* remainder of the review, never a finding it already delivered. Do not open the PR with known unresolved blocking findings; immaterial observations travel as a closing note in the PR description. --- @@ -208,8 +208,8 @@ Never call the Agent tool from inside a sub-agent context. ## Success Criteria **Mode 1 (local, primary):** -- โœ… `cr review --agent` reports **no contract blockers and no material or substantive findings** after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)`. Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar -- โœ… All material and substantive findings received before any limit are resolved and committed +- โœ… `cr review --agent` reports **no blocking findings** after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)`. Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar +- โœ… All blocking findings received before any limit are resolved and committed - โœ… No cooldown waits or retries remain when the rate-limit exception applies **Mode 2 (PR-level, fallback / optional merge gate):** diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index e071054..7ca447c 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -7,7 +7,7 @@ description: Run OpenAI Codex's AI code review LOCALLY via the `codex` CLI BEFOR This skill runs OpenAI Codex's AI code review **locally, one-shot**, against the current branch. Run it as part of pre-PR self-review โ€” after `coderabbit-review`, -before opening the PR โ€” fix every **material or substantive** finding, and open +before opening the PR โ€” fix every **blocking** finding, and open the PR once a pass produces none. Immaterial observations get one closing note and do not hold the PR: see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. @@ -260,7 +260,7 @@ than waiting it out. (see `fx-dev/skills/dev/references/scope-contract.md`). Scope first: an out-of-scope finding is deferred however material it looks (Step 3.5). Then project rules and security/privacy invariants, which block regardless of the - bar. Only what remains is ranked: material and substantive findings are fixed; + bar. Only what remains is ranked. Blocking findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go into one closing note and MUST NOT drive another iteration. @@ -285,7 +285,7 @@ than waiting it out. Run the review again after fixes, **carrying the same Scope Brief prompt plus anything newly established**. Note the iteration number in the prompt and add facts verified since the last pass, so Codex does not relitigate settled ground. -**Repeat Steps 1 โ†’ 2 until a pass produces no contract blockers and no material or substantive findings** +**Repeat Steps 1 โ†’ 2 until a pass produces no blocking findings** โ€” per the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. **Converged does NOT mean zero output.** Codex will keep producing immaterial @@ -301,14 +301,22 @@ Brief: CONVERGENCE PASS . Prior passes found issues; all fixed except . Do not re-report them. -Report findings that would change behaviour, break a build or test, make the -artifact unimplementable, or expose a security, privacy, or data-loss problem โ€” -including a leaked credential, internal URL, or private identifier in -documentation or examples. A false statement counts when a reader would act on -it; a wrong number nothing keys on does not. Also report a genuine ambiguity a -reader could act on two ways, and a missing step that would be discovered late -and cost a cycle โ€” those block too. Wording, formatting, and counts nothing keys -on: one closing note, not findings. +Report every BLOCKING finding. A finding is blocking if it is any of: + +1. A violation of a rule this project wrote down โ€” anything in AGENTS.md or + REVIEW.md, or a security or privacy invariant. Report these whatever their + direct behavioural impact; the project already decided they matter, so do not + weigh them against the bar below. +2. Something that would change behaviour, break a build or test, make the + artifact unimplementable, or expose a security, privacy, or data-loss problem + โ€” including a leaked credential, internal URL, or private identifier in + documentation or examples. A false statement counts when a reader would act + on it; a wrong number nothing keys on does not. +3. A genuine ambiguity a reader could act on two ways, or a missing step that + would be discovered late and cost a cycle. + +Wording, formatting, and counts nothing keys on are NOT blocking: one closing +note, not findings. Where this artifact declares a list illustrative and a rule authoritative, assess the RULE. A further missing list entry is not a finding. @@ -326,11 +334,14 @@ If the artifact is internally consistent and matches the tree, say so plainly. - **Cap at 4 iterations.** If Codex keeps flagging the same design decision after 4 passes, that is a human call, not more edits โ€” escalate it **by name** and stop. -- Watch the shape, and count only the tiers that block. Material or substantive - findings still arriving โ†’ keep going. A pass with none โ†’ converged, however - many immaterial observations it produced. The same disagreement twice โ†’ - escalate. -- When you stop, **report the per-pass trend** (`9, 4, 1, 0 material`) and say +- Watch the shape, and count only what blocks. Blocking findings still arriving + โ†’ keep going. A pass with none โ†’ converged, however many immaterial + observations it produced. The same disagreement twice โ†’ escalate. +- **A rising count means the last fix caused it.** If a pass returns more + blocking findings than the one before and they are all the same class, stop + fixing instances and fix the cause. When the cause is a design choice with two + defensible answers, escalate to the user rather than spending the next pass. +- When you stop, **report the per-pass trend** (`9, 4, 1, 0 blocking`) and say whether the last round's fixes were themselves reviewed. ### Step 3.5: Report out-of-scope findings, never silently apply them @@ -346,7 +357,7 @@ it before the next iteration rather than filtering by hand again. ### Step 4: Open the PR once it has converged -A **converged** Codex review โ€” a pass with no contract blockers and no material or substantive findings โ€” +A **converged** Codex review โ€” a pass with no blocking findings โ€” alongside a converged CodeRabbit review is the gate to PR creation in the SDLC (`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with unresolved material or substantive Codex findings. Outstanding **immaterial** observations do not hold diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index 44ddbd2..f17d315 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -108,7 +108,7 @@ threads, not zero observations acted on. This matters more here than anywhere else in the loop: **every push re-opens the gate.** Copilot must then re-review the new head (Step 5), so editing for an immaterial finding costs a full wait cycle and produces a fresh commit for it to -comment on. Push fixes for material and substantive findings; reply-and-resolve +comment on. Push fixes for blocking findings; reply-and-resolve the rest without a commit. ## When to Use @@ -299,7 +299,7 @@ Resolving feedback usually means pushing commits. Those commits are **unreviewed If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 4 iterations and escalate to the user if it has not settled. -**Convergence is: no contract blockers, no new material or substantive findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only material and substantive findings should produce one. +**Convergence is: no new blocking findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only blocking findings should produce one. ```bash # The gate is only passed when the newest Copilot review covers the current head. diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index cb67169..bf431e1 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -184,7 +184,7 @@ full review cycle to filter by hand. **Then freeze the implementation contract.** If the task is sourced from, names, or discovers a relevant `docs/changes/*.md` file, read it and the spec sections it links. Confirm implementation approval from the conversation or the change's recorded workflow state; if approval is unclear, STOP and ask the user. Record the contract path and approval evidence in the working brief. The change document, its linked specs, and all mandatory project rules form the implementation contract: the plan and coder prompt MUST map work to that contract and MUST NOT infer adjacent product or architecture work. -The coordinator owns one in-memory finding ledger for the run; reviewer sub-agents return findings to the coordinator and MUST NOT mutate the ledger concurrently. Give every finding a stable fingerprint (`category + file + line/range + normalized claim`) and record its source, first-seen revision, classification, materiality tier, disposition, and verification evidence. Classification and materiality are independent fields โ€” see Step 4.5 for how the tier is assigned. Classify each finding exactly once as: +The coordinator owns one in-memory finding ledger for the run; reviewer sub-agents return findings to the coordinator and MUST NOT mutate the ledger concurrently. Give every finding a stable fingerprint (`category + file + line/range + normalized claim`) and record its source, first-seen revision, classification, materiality tier, disposition, and verification evidence. Classification and materiality are independent fields โ€” see Step 4.5 for how the tier is assigned. The tier is `n/a` for a contract blocker: filter 2 stops before the bar, so a rule violation is never ranked, and inventing a tier for one is the mistake that lets it be argued down. Classify each finding exactly once as: - **required-by-contract** โ€” Necessary to satisfy the change document, its linked specs, or any mandatory project, security, privacy, test, or merge rule. - **regression-caused-by-change** โ€” A demonstrable correctness, security, privacy, or data-loss regression caused by this branch anywhere within its behavioral impact, including downstream consumers or integrations. @@ -324,10 +324,10 @@ Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeat Proceed to Step 5 when all of the following are true: -1. Every `required-by-contract` and `regression-caused-by-change` ledger entry is resolved with evidence. +1. Every **blocking** ledger entry is resolved with evidence โ€” every `required-by-contract` and `regression-caused-by-change` entry, plus every entry of any classification whose materiality tier is Material or Substantive. A judgment-originated Substantive finding can sit under `follow-up/out-of-scope` and still block; classification decides who owns it, the tier decides whether it holds the gate. 2. Contract-required tests and tests affected by the latest delta pass. 3. Every available review channel completed its initial pass or has a documented permitted degradation. -4. One verification pass over the latest affected delta produces no new finding in either blocking class. +4. One verification pass over the latest affected delta produces no new blocking finding. `follow-up/out-of-scope` entries and non-contract suggestions do not block PR creation. Limit each review channel to two remediation/delta-verification rounds after its initial pass, with four post-review fix rounds total. At the bound, create the PR if only follow-up/out-of-scope entries remain. If a blocking-class finding remains, STOP and report it to the user. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. Perfect local convergence is not required. diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 0416fa7..446b93e 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -84,14 +84,26 @@ Materiality is the **last** of three, and applying it out of order produces the 2. **Contract** โ€” is it a violation of a project rule, security or privacy invariant, or another mandatory requirement the project wrote down? Those are **blocking by virtue of being rules**, and the bar does not filter them. The project already decided they matter; that decision is not a reviewer's to re-make per finding. 3. **Materiality** โ€” for everything left, which is findings a reviewer originates from its own judgment: does acting on it change anything? -A finding that fails filter 1 is deferred. One that passes filter 2 is a **contract blocker** โ€” blocking on its own terms, and never ranked by the bar. Only what reaches filter 3 is ranked below. Contract blockers are unranked but not unblocking: convergence requires zero of them as well as zero material and substantive findings. +A finding that fails filter 1 is deferred. One that passes filter 2 is a **contract blocker** โ€” blocking on its own terms, and never ranked by the bar. Only what reaches filter 3 is ranked below. + +### Blocking โ€” the one term everything else uses + +**A finding is BLOCKING if it is any of these three:** + +1. **A contract blocker** โ€” it passed filter 2. Unranked by the bar, blocking by virtue of being a rule. +2. **Material** โ€” see the tier table below. +3. **Substantive** โ€” see the tier table below. + +**An immaterial finding is never blocking.** That is the whole vocabulary: *blocking* or *immaterial*. + +Every downstream rule is written in terms of **blocking**, deliberately. Fix blocking findings; push for blocking findings; keep going while blocking findings arrive; converge when a pass produces none. Skills MUST NOT re-enumerate the three constituents in their own stopping rules โ€” an enumeration copied to a dozen sites silently goes stale the moment this list changes, and every copy then under-blocks. Say "blocking" and point here. ### The bar | Tier | Examples | Treatment | |---|---|---| -| **Material** | Wrong behaviour, data loss, security, a build/CI/test that will fail, a contradiction that makes the artifact unimplementable, a stated fact that is false **and that a reader would act on** | Report individually. Blocks convergence. | -| **Substantive** | A genuine ambiguity a reader could act on two ways; a missing step that would be discovered late and cost a cycle | Report individually. Blocks convergence. | +| **Material** | Wrong behaviour, data loss, security, a build/CI/test that will fail, a contradiction that makes the artifact unimplementable, a stated fact that is false **and that a reader would act on** | Report individually. **Blocking.** | +| **Substantive** | A genuine ambiguity a reader could act on two ways; a missing step that would be discovered late and cost a cycle | Report individually. **Blocking.** | | **Immaterial** | Wording that is merely improvable; a count off by one where nothing keys on the count; a list entry missing from a list nothing enumerates exhaustively; formatting; a synonym that reads better | **One closing note, unnumbered, non-blocking.** Never a separate finding, never a reason for another pass. | When unsure which tier something is, ask: *if this shipped uncorrected, what breaks?* If the honest answer is "nothing, it is just not as good as it could be", it is immaterial. @@ -104,22 +116,23 @@ When unsure which tier something is, ask: *if this shipped uncorrected, what bre **A decision already made, recorded, and reasoned โ€” where the disagreement is about preference.** Once an artifact states a decision with its rationale, including a rationale that says "we do not know, and here is how we will find out", re-arguing it is not a review finding. Say so once as a single escalation; do not re-raise it on the next pass in different words. A preference re-litigated across passes needs a person, not another round. -**But a recorded rationale does not make a decision safe.** If the decision *itself* is the defect โ€” it leaks a credential, loses data, violates a security or privacy invariant, or contradicts a contract the project mandates โ€” it is a **Material finding and stays blocking until resolved**, however carefully it is reasoned. Writing down why you did an unsafe thing does not make it safe. The distinction is whether you disagree with the choice or the choice is wrong: the first is an escalation, the second is a finding. +**But a recorded rationale does not make a decision safe.** If the decision *itself* is the defect โ€” it leaks a credential, loses data, violates a security or privacy invariant, or contradicts a contract the project mandates โ€” it is **blocking and stays blocking until resolved**, however carefully it is reasoned. Which kind of blocking follows the filters, and you do not need to decide it to act: where it violates a rule the project wrote down it is a contract blocker at filter 2; where it is your own reading of a defect it is Material at filter 3. Both block, so the disposition is identical. Writing down why you did an unsafe thing does not make it safe. The distinction is whether you disagree with the choice or the choice is wrong: the first is an escalation, the second is a finding. **An artifact that admits a limit.** "This is verified by X at implementation time", "this is an open question gated on Y" โ€” those are dispositions, not gaps. Check the gate is real and sequenced before the thing that depends on it; do not report the limit itself. ### Convergence -**A review has converged when a pass produces no unresolved contract blockers and no material or substantive findings โ€” not when it produces zero output.** Zero is usually unreachable and waiting for it burns cycles on immaterial churn. An outstanding contract blocker prevents convergence by itself: it is unranked precisely because the project already decided it blocks, so "no material or substantive findings" never clears it. +**A review has converged when a pass produces no unresolved blocking findings โ€” not when it produces zero output.** Zero is usually unreachable and waiting for it burns cycles on immaterial churn. Track findings per pass and watch the shape, not just the count: -- **Contract blockers outstanding** โ€” not converged, whatever the tiers did. -- **Material or substantive findings still arriving, in new categories** โ€” keep going. The review is still working. -- **No contract blockers and no material or substantive findings this pass** โ€” converged, whatever the immaterial count did. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. +- **Blocking findings still arriving, in new categories** โ€” keep going. The review is still working. +- **No blocking findings this pass** โ€” converged, whatever the immaterial count did. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. - **The same disagreement in successive passes** โ€” stop. That is a human decision, not a review outcome. Escalate it by name. -Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 material. Stopping โ€” three immaterial wording items remain, listed below."* +Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 blocking. Stopping โ€” three immaterial wording items remain, listed below."* + +**A rising count is a divergence signal, not progress.** If a pass produces more blocking findings than the one before it and they are all the same class, the last round's fix is generating them. Stop and address the cause rather than the instances โ€” and if the cause is a design choice with two defensible answers, that is an escalation to the user, not a fifth pass. **State honestly what the last pass did not cover.** If you stop after applying fixes that were never themselves reviewed, say so. diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index e3093d0..7452a18 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -44,7 +44,7 @@ Then review within it: Scope decides whether a finding is the author's problem. **Materiality decides whether it is worth their time** โ€” see the materiality bar in the same reference. -- **Report material and substantive findings individually.** Wrong behaviour, +- **Report every blocking finding individually.** Wrong behaviour, data loss, security, a build or test that will fail, a contradiction that makes the change unimplementable, a stated fact that is false and that a reader would act on, or a genuine ambiguity diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 8b8f2ed..6d0aa88 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -25,7 +25,7 @@ Scope decides whether a finding is *ours*. Materiality decides whether it is *wo An in-scope item that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is an **observation, not a finding**, and is **resolved by replying with that reasoning** rather than by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. -This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for material and substantive findings; reply-and-resolve the rest in the same cycle. +This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for blocking findings โ€” every contract blocker, Material and Substantive finding; reply-and-resolve only the immaterial rest in the same cycle. A contract blocker is never discharged by a reply explaining it: the artifact has to change. **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the code, and when it does not hold, reply with the evidence and resolve rather than changing working code to satisfy a misreading. @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no contract blockers and no new material or substantive findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. +6. Stop when a pass produces **no new blocking findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. @@ -287,7 +287,7 @@ If unresolved threads remain, report which reviewers still have open feedback. 1. All unresolved automated review threads identified โ€” matched on the `copilot-pull-request-reviewer` login, **not** the bare `Copilot` โ€” **plus** any findings in the "Suppressed comments" block of every Copilot review **of the current head commit**, which produce no threads 2. Appropriate resolver skill(s) invoked (Copilot + CodeRabbit in parallel where applicable) -3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, produced no contract blockers and no new material or substantive findings, and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence +3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, produced no new blocking findings, and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence 4. CodeRabbit's check is in a terminal passing state (or absent if not configured) 5. Final verification confirms all threads resolved 6. Summary output provided From 070e69fec0aa33d2dcdaf0ac9099553d52fdd96f Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 17:44:23 +0000 Subject: [PATCH 15/48] fix(fx-dev): close the last two stopping-rule inconsistencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 4 returned 2 findings against pass 3's 8. Both verified and fixed. codex-review's Step 4 PR gate still read "unresolved material or substantive Codex findings", so a contract blocker could pass it while the sentence directly above required no blocking findings. The previous commit's sweep missed it because the phrase wraps across a line break and the grep that verified the sweep was line-oriented. Re-scanned whitespace-insensitively; this was the only survivor. The PR-ready stopping condition introduced a combination the workflow could neither clear nor waive: a Substantive finding filed as `follow-up/out-of-scope` blocked the gate, while Steps 2.5 and 4.5 forbid implementing that class and the bound rule permits shipping when only that class remains. The pair is now stated to be illegal, with the reason โ€” classification and tier are assigned by different filters and cannot disagree. An out-of-scope finding is deferred at filter 1 and never reaches the bar, so its tier is `n/a`; anything that reaches the bar is in scope by construction, so a Material or Substantive rank means it belongs in a blocking class. Recording the pair means a filter was misapplied. This is the existing "materiality never promotes an out-of-scope finding back into scope" rule applied to the ledger. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 4 ++-- plugins/fx-dev/skills/dev/SKILL.md | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 73366cb..1e5d801 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.23.0", + "version": "0.23.1", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 108785d..9b85166 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": "3.10.0", + "version": "3.10.1", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 7ca447c..162f8d4 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -359,8 +359,8 @@ it before the next iteration rather than filtering by hand again. A **converged** Codex review โ€” a pass with no blocking findings โ€” alongside a converged CodeRabbit review is the gate to PR creation in the SDLC -(`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with unresolved material or -substantive Codex findings. Outstanding **immaterial** observations do not hold +(`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with any unresolved +blocking Codex finding. Outstanding **immaterial** observations do not hold the PR; carry them into its description as a closing note. ## When to Use This Skill diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index bf431e1..2b98ca1 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -324,7 +324,9 @@ Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeat Proceed to Step 5 when all of the following are true: -1. Every **blocking** ledger entry is resolved with evidence โ€” every `required-by-contract` and `regression-caused-by-change` entry, plus every entry of any classification whose materiality tier is Material or Substantive. A judgment-originated Substantive finding can sit under `follow-up/out-of-scope` and still block; classification decides who owns it, the tier decides whether it holds the gate. +1. Every **blocking** ledger entry is resolved with evidence โ€” every `required-by-contract` and `regression-caused-by-change` entry, and every entry whose materiality tier is Material or Substantive. + + **`follow-up/out-of-scope` with a Material or Substantive tier is not a legal combination.** The two fields are assigned by different filters and cannot disagree: an out-of-scope finding is deferred at filter 1 and never reaches the bar, so its tier is `n/a`; a finding that *does* reach the bar is in scope by construction, so if it ranks Material or Substantive it is a defect in work this change actually did, and belongs under `required-by-contract` or `regression-caused-by-change`. If you are about to record that pair, one of the two filters was misapplied โ€” re-run them rather than writing an entry the gate can neither clear nor waive. Materiality never promotes an out-of-scope finding back into scope (`references/scope-contract.md` ยง Three filters); this rule is that principle applied to the ledger. 2. Contract-required tests and tests affected by the latest delta pass. 3. Every available review channel completed its initial pass or has a documented permitted degradation. 4. One verification pass over the latest affected delta produces no new blocking finding. From 423aedf3f23c567346531b92aef0f44df01a142b Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 18:18:00 +0000 Subject: [PATCH 16/48] feat(fx-dev): raise the review iteration bound from 4 to 15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cap was doing the wrong job. At 4 it was being hit by loops that were still making progress, which turned "stopped" into something an agent could report as a pass โ€” and a bound reached is not a review converged. This branch's own test run hit it: pass 4 was the last permitted iteration while the trend was still falling, so its fixes went unreviewed. Convergence is now the stated goal and the bound is a runaway backstop. 15 is deliberately far above what a healthy loop needs, so reaching it means something is wrong rather than that the work was large. Defined once, in scope-contract.md ยง The iteration bound, and referenced from the six loops that previously carried their own number: coderabbit-review Mode 1 and Mode 2, codex-review, copilot-review, resolve-pr-feedback, and dev's per-channel remediation bound. Skills MUST NOT set their own โ€” the same consolidation the blocking term got, for the same reason. Raising a bound is only safe if the early exits do the real work, so they are restated at every site: converged, the same disagreement twice, and a rising blocking count of one class all fire in single digits, and the bound only catches a loop none of them caught. The headroom is explicitly not a budget โ€” a round that resolves only immaterial items is churn at iteration 13 exactly as at iteration 3. Two consequences are stated plainly rather than left implied. Stopping at the bound is an escalation reported with the per-pass trend and what remains unresolved, never a clean result. And an unattended loop that genuinely runs to 15 can take hours, since a reviewer pass on a real branch takes ten minutes or more; that is the intended trade, and a reason to fix causes over instances. Also drops "Perfect local convergence is not required" from dev's stopping condition, which contradicted the goal outright, and tightens the bound's escape hatch: the PR opens at the bound only if every remaining entry is follow-up/out-of-scope with tier n/a, never with a blocking entry open. The CI and verification fix loops (dev Steps 6.2, 7.4) keep their separate 2- and 3-attempt bounds. They retry a failing command rather than converging a review, so the 15-iteration rule does not apply to them. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 4 ++-- plugins/fx-dev/skills/codex-review/SKILL.md | 10 ++++++--- plugins/fx-dev/skills/copilot-review/SKILL.md | 2 +- plugins/fx-dev/skills/dev/SKILL.md | 4 +++- .../skills/dev/references/scope-contract.md | 22 +++++++++++++++++-- .../skills/resolve-pr-feedback/SKILL.md | 2 +- 8 files changed, 36 insertions(+), 12 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 1e5d801..c4e4fa2 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.23.1", + "version": "0.24.0", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 9b85166..787a19d 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": "3.10.1", + "version": "3.11.0", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 0e16f4a..36d8ffe 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -112,7 +112,7 @@ Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass p **Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. -- **Cap at 4 iterations.** If CodeRabbit keeps flagging the same design decision after 4 passes, that is a human call, not more code edits โ€” escalate it **by name** and stop. +- **Cap at 15 iterations** (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) โ€” a runaway backstop, not a target. Convergence is the goal; reaching the bound is a failure to converge, and you report it as an escalation rather than a pass. If CodeRabbit keeps flagging the same design decision across two passes, that is a human call, not more code edits โ€” escalate it **by name** and stop, without spending the remaining iterations. - Watch the shape, and count only what blocks: blocking findings still arriving โ†’ keep going; a pass with none โ†’ converged, however many immaterial observations it produced; the same disagreement twice โ†’ escalate. - **Rate-limit exception:** stop immediately on throttling; do not consume iterations waiting for cooldowns. - When you stop, report the per-pass trend and whether the last round's fixes were themselves reviewed. @@ -187,7 +187,7 @@ query { If this count is 0 AND the CodeRabbit check is `success`, the gate is PASSED. -**Cap the loop at 4 iterations** โ€” if CodeRabbit is still posting new feedback after 4 wait+resolve cycles, escalate to the user. Almost always this means CodeRabbit and the codebase disagree on a design decision that needs human input. +**Cap the loop at 15 iterations** (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) โ€” if CodeRabbit is still posting new blocking feedback at the bound, escalate to the user and say the loop did not converge. Almost always a loop that runs that long means CodeRabbit and the codebase disagree on a design decision that needs human input โ€” which the same-disagreement-twice rule should have caught far earlier. Escalate when you see it, not at iteration 15. **Threads must all be resolved, but resolution is not the same as a fix.** An immaterial thread is resolved by replying with the reason it is not being actioned โ€” the gate is zero *unresolved* threads, not zero observations acted on. Re-pushing for another CodeRabbit pass to chase immaterial items is exactly the churn the materiality bar exists to stop. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 162f8d4..f1eb6f0 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -331,9 +331,13 @@ report it as a blocking finding however carefully it is reasoned. If the artifact is internally consistent and matches the tree, say so plainly. ``` -- **Cap at 4 iterations.** If Codex keeps flagging the same design decision after - 4 passes, that is a human call, not more edits โ€” escalate it **by name** and - stop. +- **Cap at 15 iterations** (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound). The + bound is a runaway backstop, not a target โ€” convergence is the goal, and + stopping at the bound is a failure to converge that you report as an + escalation, never as a clean pass. If Codex flags the same design decision in + two successive passes, that is a human call, not more edits โ€” escalate it **by + name** and stop rather than spending the remaining iterations. Each Codex pass + on a real branch takes many minutes, so fix causes, not instances. - Watch the shape, and count only what blocks. Blocking findings still arriving โ†’ keep going. A pass with none โ†’ converged, however many immaterial observations it produced. The same disagreement twice โ†’ escalate. diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index f17d315..72c81ac 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -297,7 +297,7 @@ on its own never passes it (**D4**). Resolving feedback usually means pushing commits. Those commits are **unreviewed**, and Copilot will not look at them by itself. -If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 4 iterations and escalate to the user if it has not settled. +If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 15 iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if it has not settled. The bound is a backstop, not a budget: escalate as soon as the same disagreement repeats or the blocking count rises, and remember that every iteration here costs a full Copilot wait cycle. **Convergence is: no new blocking findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only blocking findings should produce one. diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 2b98ca1..7c1c771 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -331,7 +331,9 @@ Proceed to Step 5 when all of the following are true: 3. Every available review channel completed its initial pass or has a documented permitted degradation. 4. One verification pass over the latest affected delta produces no new blocking finding. -`follow-up/out-of-scope` entries and non-contract suggestions do not block PR creation. Limit each review channel to two remediation/delta-verification rounds after its initial pass, with four post-review fix rounds total. At the bound, create the PR if only follow-up/out-of-scope entries remain. If a blocking-class finding remains, STOP and report it to the user. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. Perfect local convergence is not required. +`follow-up/out-of-scope` entries and non-contract suggestions do not block PR creation. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. At the bound, create the PR only if every remaining entry is `follow-up/out-of-scope` with tier `n/a`. If a blocking entry remains, STOP and report it to the user โ€” the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. + +**Do not spend the headroom.** The bound is far above what a healthy channel needs; the exits that should actually end a loop โ€” converged, the same disagreement twice, a rising blocking count of one class โ€” all fire in single digits. A round that resolves only immaterial items is churn at any iteration number, and the convergence rule already forbids it. --- diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 446b93e..0a28d6c 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -132,9 +132,27 @@ Track findings per pass and watch the shape, not just the count: Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 blocking. Stopping โ€” three immaterial wording items remain, listed below."* -**A rising count is a divergence signal, not progress.** If a pass produces more blocking findings than the one before it and they are all the same class, the last round's fix is generating them. Stop and address the cause rather than the instances โ€” and if the cause is a design choice with two defensible answers, that is an escalation to the user, not a fifth pass. +**A rising count is a divergence signal, not progress.** If a pass produces more blocking findings than the one before it and they are all the same class, the last round's fix is generating them. Stop and address the cause rather than the instances โ€” and if the cause is a design choice with two defensible answers, that is an escalation to the user, not another pass. -**State honestly what the last pass did not cover.** If you stop after applying fixes that were never themselves reviewed, say so. +**State honestly what the last pass did not cover.** If you stop after applying fixes that were never themselves reviewed, say so. A loop that stops at the bound below has *not* converged, and reporting it as clean is a false result. + +### The iteration bound + +**Convergence is the goal. The bound is a runaway backstop, not a target, and reaching it is a failure to converge โ€” never a stopping condition you are entitled to treat as success.** + +**Every review-convergence loop caps at 15 iterations.** That is the single number; skills MUST NOT set their own. It is deliberately far above what a healthy loop needs โ€” a review that is working converges in single digits โ€” so that hitting it means something is wrong rather than that the work was merely large. + +A loop should almost always end on one of the three signals above, all of which fire long before 15: + +- **Converged** โ€” a pass with no blocking findings. The only successful exit. +- **The same disagreement twice** โ€” escalate by name. Do not spend the remaining iterations re-arguing it. +- **A rising count of one class** โ€” the last fix is generating them. Fix the cause, or escalate the design choice. + +Those exits are what keep the loop short; the bound only catches a loop none of them caught. **Do not treat the headroom as licence to keep going** โ€” an iteration that fixes only immaterial items is churn whether it is the 3rd or the 13th, and the convergence rule already forbids it. + +When you stop at the bound, say so explicitly, report the per-pass trend, name what is still unresolved, and hand it to the user. "Reached 15 iterations" is an escalation, not a pass. + +**Cost is real and worth stating.** A single reviewer pass on a substantial branch can take ten minutes or more, so an unattended loop that actually runs to 15 can consume hours. That is the intended trade โ€” correctness over speed โ€” but it is a reason to fix causes rather than instances, not a budget to spend. ## The sprawl stop rule diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 6d0aa88..46847ca 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no new blocking findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 4 outer iterations and escalate to the user if not converged. +6. Stop when a pass produces **no new blocking findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 15 outer iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if not converged โ€” reaching the bound is a failure to converge, reported as such, not a pass. Escalate earlier on a repeated disagreement or a rising blocking count rather than spending the remaining iterations. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. From 4e657af095b851c855f5be3ce1d73d7c572a397d Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 18:54:47 +0000 Subject: [PATCH 17/48] fix(fx-dev): retire the last pre-bar fix directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 5 returned 3 findings. Trend: 4, 4, 8, 2, 3. All verified. Two were caused by the previous commit. dev's Step 6.3 bounded-delta block still said "Allow at most two remediation rounds per reviewer" โ€” a second, lower bound sitting downstream of the new canonical one, so the automated review loop would have escalated at two rounds regardless of the 15 the contract defines. It now defers to the contract like every other loop. And the rising-count escalation was restated at two sites without the qualifier the canonical rule carries. "Escalate when the blocking count rises" is wrong: divergence is a rising count whose new findings are all one class, and the response is to fix that cause, not to stop. A count rising across different categories means the review is still productive. Both sites now say so. The third predates the bar. Six directives still told the agent to fix "actionable" or "valid" concerns โ€” coderabbit-review's rate-limit paths, its Step 2 heading and Mode 2 success criterion, and copilot-review's resolver summary, suppressed-comment triage and completion criterion. A correct but immaterial wording observation is "valid" under those words and immaterial under the bar, so an agent could edit and push it and reopen the loop it was supposed to be closing. All six now say blocking, and the ones about thread resolution say explicitly that an immaterial observation is resolved by reply rather than by an edit. The skill description said "actionable" too and now matches. Swept the same wording across every fx-dev skill, including ones this branch does not otherwise touch: no other occurrence exists, so nothing outside the change needed editing. The two "always actionable" lines that remain are about scope โ€” security, data-loss and correctness are always in scope โ€” not about what clears the bar. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 10 +++++----- plugins/fx-dev/skills/copilot-review/SKILL.md | 11 ++++++----- plugins/fx-dev/skills/dev/SKILL.md | 2 +- plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c4e4fa2..ab1dd14 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.24.0", + "version": "0.24.1", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 787a19d..d7b75e3 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": "3.11.0", + "version": "3.11.1", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 36d8ffe..e464c4d 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -1,13 +1,13 @@ --- name: coderabbit-review -description: "Run CodeRabbit's optional AI review. PRIMARY path: run it LOCALLY via the `cr` CLI before opening a PR and resolve actionable findings. FALLBACK path: wait for + resolve its automated PR review when available. Pass a Scope Brief as args โ€” findings MUST be triaged against the user's original request. Rate limits degrade gracefully: report once, skip CodeRabbit, and continue the SDLC." +description: "Run CodeRabbit's optional AI review. PRIMARY path: run it LOCALLY via the `cr` CLI before opening a PR and resolve blocking findings. FALLBACK path: wait for + resolve its automated PR review when available. Pass a Scope Brief as args โ€” findings MUST be triaged against the user's original request. Rate limits degrade gracefully: report once, skip CodeRabbit, and continue the SDLC." --- # CodeRabbit Review CodeRabbit reviews code with AI. The **primary** way to use it is **locally, via the `cr` CLI, BEFORE opening a PR** โ€” as part of pre-PR self-review, alongside `/review` and `/simplify`. Prefer a clean local result when the service is available. A **fallback** path handles CodeRabbit's PR-level review for repos where its GitHub App is configured to auto-review PRs. -**IMPORTANT โ€” CodeRabbit is optional when rate-limited.** If the CLI, API, GitHub check, or wait script reports a CodeRabbit quota/rate limit, report it once and continue without CodeRabbit. Do not sleep, poll, retry after a cooldown, ask the user to wait, or block PR creation/merge solely on CodeRabbit throttling. Resolve actionable findings already received before the limit, then mark the CodeRabbit pass as `skipped (rate-limited)`. This exception applies only to CodeRabbit; it does not relax Copilot, CI, tests, or other merge gates. +**IMPORTANT โ€” CodeRabbit is optional when rate-limited.** If the CLI, API, GitHub check, or wait script reports a CodeRabbit quota/rate limit, report it once and continue without CodeRabbit. Do not sleep, poll, retry after a cooldown, ask the user to wait, or block PR creation/merge solely on CodeRabbit throttling. Resolve blocking findings already received before the limit, then mark the CodeRabbit pass as `skipped (rate-limited)`. Immaterial observations are not resolved by editing โ€” actioning one manufactures the next round's input. This exception applies only to CodeRabbit; it does not relax Copilot, CI, tests, or other merge gates. ## โ›” Local-First: Run CodeRabbit BEFORE Opening the PR @@ -94,9 +94,9 @@ Use `cr review --agent --base main` to scope to the branch's diff against `main` - If `cr` reports it is **not authenticated**, **STOP and report to the user** โ€” the workspace is expected to be authed. **Do NOT run `cr auth login`** (it is interactive). Do not work around it. - If `cr` is **not installed / unavailable**, skip to Mode 2 (resolve at the PR level after opening) and report this to the user once. -- If `cr` reports a **rate limit, quota limit, or cooldown**, stop the CodeRabbit loop immediately. Report the skip once, resolve any actionable findings already returned, and continue to PR creation without requiring a clean rerun. +- If `cr` reports a **rate limit, quota limit, or cooldown**, stop the CodeRabbit loop immediately. Report the skip once, resolve any blocking findings already returned, and continue to PR creation without requiring a clean rerun. -### Step 2: Resolve every actionable finding +### Step 2: Resolve every blocking finding Treat findings like self-review feedback: @@ -214,5 +214,5 @@ Never call the Agent tool from inside a sub-agent context. **Mode 2 (PR-level, fallback / optional merge gate):** - โœ… CodeRabbit check is terminal with a passing conclusion and all threads are resolved, **or** CodeRabbit rate-limited and the gate is recorded as `skipped (rate-limited)` -- โœ… Any valid concerns already received are fixed and pushed +- โœ… Any **blocking** findings already received are fixed and pushed. A correct-but-immaterial observation is resolved by reply, not by an edit โ€” editing it reopens the review loop - โœ… CodeRabbit throttling alone does not block merge diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index 72c81ac..f7e7c46 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -231,8 +231,9 @@ Then: 1. `grep -i 'Suppressed comments'` the bodies. If present, **read the entire `
` block** โ€” every item, not just the summary count. -2. Triage each item exactly like a thread comment: fix what is valid, apply the - Scope Brief to what is out of scope. +2. Triage each item exactly like a thread comment: fix what is blocking, reply + to what is merely correct-but-immaterial, and apply the Scope Brief to what is + out of scope. 3. **Do not apply a suppressed suggestion on sight.** One observed suppressed comment, applied as written, would have introduced the very bug it claimed to report. Verify the finding against the code before changing anything. @@ -250,7 +251,7 @@ Skill tool: skill="fx-dev:resolve-pr-feedback", args="" This skill will: 1. Find all unresolved Copilot threads 2. Categorize each (nitpick, valid, incorrect, outdated, deferred) -3. Fix valid concerns, reply to and resolve all threads +3. Fix **blocking** findings; reply-and-resolve every other thread without editing 4. Report a summary table of actions taken ### Step 4: Confirm Resolution @@ -297,7 +298,7 @@ on its own never passes it (**D4**). Resolving feedback usually means pushing commits. Those commits are **unreviewed**, and Copilot will not look at them by itself. -If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 15 iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if it has not settled. The bound is a backstop, not a budget: escalate as soon as the same disagreement repeats or the blocking count rises, and remember that every iteration here costs a full Copilot wait cycle. +If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 15 iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if it has not settled. The bound is a backstop, not a budget: escalate as soon as the same disagreement repeats, and when the blocking count rises **with every new finding in one class**, fix that cause before escalating โ€” a count that rises across different categories means the review is still productive. Every iteration here costs a full Copilot wait cycle. **Convergence is: no new blocking findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only blocking findings should produce one. @@ -317,7 +318,7 @@ This skill is complete when ALL of: - โœ… Copilot review has been received (script exited 0) **for the current head commit** โ€” `REVIEWED_COMMIT_ID` equals `PR_HEAD_SHA`, checked by you, not for an earlier commit - โœ… The script's `SUPPRESSED_COMMENTS=` line was read, and it is a definite `0` or a `1` whose block has been read in full and every item triaged (Step 2b). A `0` must be confirmed from the output rather than assumed, and **`unknown` does not satisfy this criterion at all** โ€” the check failed to run (**D5**), so re-run the waiter or triage the bodies by hand before claiming the gate - โœ… All Copilot threads resolved (0 unresolved, **filtered to the Copilot login**) -- โœ… Any valid code concerns have been fixed and pushed โ€” **and the resulting head was itself reviewed** +- โœ… Any **blocking** findings have been fixed and pushed โ€” **and the resulting head was itself reviewed**. Correct-but-immaterial observations are resolved by reply and produce no push, so they owe no further pass **Never report this gate as passed on the grounds that polling found no new feedback.** Absence of a review is not a clean review, and a timeout (exit 1) is not a verdict. Silence here is an unasked question, not an answer. diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 7c1c771..acd8b54 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -653,7 +653,7 @@ If `Bash` `run_in_background` isn't available in your context, fall back to full ##### Bounded delta review (both modes) -Fix only blocking-class findings. Record follow-up/out-of-scope feedback without implementing it, and settle its thread with an out-of-scope disposition when repository policy permits. Allow at most two remediation rounds per reviewer. At the bound, continue when only follow-up/out-of-scope findings remain; escalate any unresolved blocking-class finding to the user. Do not seek zero suggestions or restart unrelated review channels. +Fix only blocking-class findings. Record follow-up/out-of-scope feedback without implementing it, and settle its thread with an out-of-scope disposition when repository policy permits. Each reviewer's remediation loop caps at the canonical bound in `references/scope-contract.md` ยง The iteration bound โ€” 15 rounds, the single number no local instruction overrides. Convergence, not the bound, is what should end it: the early exits (converged, the same disagreement twice, a rising blocking count of one class) all fire in single digits. At the bound, continue when only follow-up/out-of-scope findings remain; escalate any unresolved blocking-class finding to the user. Do not seek zero suggestions or restart unrelated review channels. ##### Skip rules diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 46847ca..b287be8 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -203,7 +203,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no new blocking findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 15 outer iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if not converged โ€” reaching the bound is a failure to converge, reported as such, not a pass. Escalate earlier on a repeated disagreement or a rising blocking count rather than spending the remaining iterations. +6. Stop when a pass produces **no new blocking findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 15 outer iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if not converged โ€” reaching the bound is a failure to converge, reported as such, not a pass. Escalate earlier on a repeated disagreement rather than spending the remaining iterations; and when the blocking count rises **with every new finding in one class**, fix that cause first โ€” a count rising across different categories means the review is still productive, not diverging. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. From 08bd878857ea659a47532239a57c38f292fedc57 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 19:13:42 +0000 Subject: [PATCH 18/48] fix(fx-dev): close eight materiality-bar gaps codex pass 6 found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trend: 4, 4, 8, 2, 3, 8 blocking. The count rose, but across six different classes rather than one, so by the branch's own divergence rule the review is still productive and the loop continues. Correcting the previous commit's claim: it said the "actionable"/"valid" sweep found no other occurrence. That was wrong. The grep that verified it filtered codex-review out of its own results, so codex-review's Step 2 heading still said "Resolve every actionable finding". Re-swept unfiltered; that heading and copilot-feedback-resolver's triage table were the survivors, and both are fixed here. The most serious finding is a privacy hole in the contract itself. Filter 1 runs before filter 2, and the always-in-scope carve-out that stops a brief excluding a real defect named security, data-loss and correctness but omitted privacy. A brief excluding a privacy defect would therefore have had it deferred at filter 1 before filter 2 could recognise the invariant โ€” despite AGENTS.md making private-information leaks a project rule. Privacy is now in the carve-out and in all four downstream copies. The rest: - resolve-pr-feedback stopped on no *new* blocking findings, so a blocker carried from an earlier pass, or a suppressed Copilot item that creates no thread, could sit unresolved while every thread showed resolved. Both its loop and its success criterion now require no *unresolved* blocking finding. - pr-reviewer's Standards section still said "BLOCK: security, bugs, vendor reuse violations", narrower than the blocking definition it now reports against, so a Substantive ambiguity could be approved under one heading and blocked under the other. - resolve-pr-feedback's Step 4 dispatched resolvers by bare skill name, carrying neither the Scope Brief nor per-thread dispositions, so the materiality triage above it could not reach the resolver actually acting. Each invocation now carries both. - copilot-feedback-resolver routed every "valid" concern to a coder sub-agent. Split into valid-blocking (fix) and valid-immaterial (reply and resolve, do not edit). This file is not otherwise part of this change, but the bar made it inconsistent, so it is a regression this change caused. - The suppressed-comment triage step told the agent to reply to immaterial items four lines above the note that suppressed findings create no thread to reply to. It now says to record the disposition instead. That contradiction was introduced by the previous commit. - The PR-ready gate re-enumerated the ledger classes and tiers right after the contract forbade stopping rules from restating blocking. It now says "blocking" and leaves the mapping to the paragraph below it. - Filter 1 and the never-suppress rule disagreed about an excluded observation that is correct: "the implementation is absent" is true of a docs-only change and still out of scope. Only a defect in work the change actually did, or an exclusion that was never valid, re-enters scope. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 2 +- .../skills/copilot-feedback-resolver/SKILL.md | 3 ++- plugins/fx-dev/skills/copilot-review/SKILL.md | 10 +++++----- plugins/fx-dev/skills/dev/SKILL.md | 2 +- .../skills/dev/references/scope-contract.md | 4 +++- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 6 +++--- .../skills/resolve-pr-feedback/SKILL.md | 19 ++++++++++++++++--- 10 files changed, 34 insertions(+), 18 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ab1dd14..02ad736 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.24.1", + "version": "0.24.2", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index d7b75e3..708b838 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": "3.11.1", + "version": "3.11.2", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index e464c4d..9ea96fd 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -69,7 +69,7 @@ coordinator's ledger. **The brief never suppresses a real finding.** It excludes work deliberately not done; it does not excuse defects in the work that *was* done. Security, -data-loss, and correctness problems inside the change are always actionable. If a +**privacy**, data-loss, and correctness problems inside the change are always in scope. If a finding the brief excluded turns out to be correct, the exclusion was wrong โ€” fix the work and correct the brief. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index f1eb6f0..367f56a 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -254,7 +254,7 @@ If the run produces **no output for several minutes at near-0% CPU**, it is stalled, not slow โ€” diagnose it with the rollout check in "How to Run" rather than waiting it out. -### Step 2: Resolve every actionable finding +### Step 2: Resolve every blocking finding - **Triage in the contract's order โ€” scope, then contract, then materiality** (see `fx-dev/skills/dev/references/scope-contract.md`). Scope first: an diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index f869738..48cf934 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -170,7 +170,8 @@ For each unresolved Copilot comment: | **Nitpick** | Contains `[nitpick]` prefix | Auto-resolve immediately | | **Outdated** | Refers to code that no longer exists | Reply with explanation, resolve | | **Incorrect** | Misunderstands project conventions | Reply with explanation, resolve, update `REVIEW.md` | -| **Valid** | Current, actionable concern | Delegate to coder sub-agent to fix | +| **Valid โ€” blocking** | Current concern that clears the bar: a project-rule, security or privacy violation, or a Material or Substantive finding (`fx-dev/skills/dev/references/scope-contract.md`) | Delegate to coder sub-agent to fix | +| **Valid โ€” immaterial** | Correct, but would change nothing if it shipped uncorrected | Reply with that reasoning, resolve. **Do not edit** โ€” a fix push reopens the review loop for an item that changes nothing | | **Deferred** | Valid but out of scope for this PR | Track in PROJECT.md, reply, resolve | ### 3. Resolve Threads diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index f7e7c46..9a100d2 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -93,8 +93,8 @@ not handed it. silently dropped. - **The brief never suppresses a real finding.** It excludes work deliberately not done; it does not excuse defects in the work that *was* done. Security, - data-loss, and correctness problems inside the change are always actionable and - always block the merge gate. + **privacy**, data-loss, and correctness problems inside the change are always in + scope and always block the merge gate. - Copilot being unable to see the scope is not a reason to widen the change. Implementing its out-of-scope suggestions is scope creep with a reviewer's name on it. @@ -231,9 +231,9 @@ Then: 1. `grep -i 'Suppressed comments'` the bodies. If present, **read the entire `
` block** โ€” every item, not just the summary count. -2. Triage each item exactly like a thread comment: fix what is blocking, reply - to what is merely correct-but-immaterial, and apply the Scope Brief to what is - out of scope. +2. Triage each item exactly like a thread comment: fix what is blocking, and + record โ€” do not reply, there is no thread to reply to (item 4) โ€” the + disposition for what is merely correct-but-immaterial or out of scope. 3. **Do not apply a suppressed suggestion on sight.** One observed suppressed comment, applied as written, would have introduced the very bug it claimed to report. Verify the finding against the code before changing anything. diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index acd8b54..c669168 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -324,7 +324,7 @@ Do not restart the full matrix merely because `HEAD` changed. Deduplicate repeat Proceed to Step 5 when all of the following are true: -1. Every **blocking** ledger entry is resolved with evidence โ€” every `required-by-contract` and `regression-caused-by-change` entry, and every entry whose materiality tier is Material or Substantive. +1. Every **blocking** ledger entry is resolved with evidence. Blocking is defined once, in `references/scope-contract.md` ยง Blocking; this gate does not restate it. **`follow-up/out-of-scope` with a Material or Substantive tier is not a legal combination.** The two fields are assigned by different filters and cannot disagree: an out-of-scope finding is deferred at filter 1 and never reaches the bar, so its tier is `n/a`; a finding that *does* reach the bar is in scope by construction, so if it ranks Material or Substantive it is a defect in work this change actually did, and belongs under `required-by-contract` or `regression-caused-by-change`. If you are about to record that pair, one of the two filters was misapplied โ€” re-run them rather than writing an entry the gate can neither clear nor waive. Materiality never promotes an out-of-scope finding back into scope (`references/scope-contract.md` ยง Three filters); this rule is that principle applied to the ledger. 2. Contract-required tests and tests affected by the latest delta pass. diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 0a28d6c..3168345 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -68,7 +68,7 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i **A reviewer invoked without a brief MUST reconstruct one** from the conversation before reviewing, and say that it did. Reviewing a diff with no idea what was asked for is the failure mode this contract exists to prevent โ€” never proceed as if the diff speaks for itself. -**Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, data-loss, and correctness problems inside the change are always in scope, whatever the brief says. If a reviewer flags something excluded and is *right* โ€” the exclusion was wrong โ€” fix the work and correct the brief. +**Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always in scope, whatever the brief says โ€” a brief cannot exclude them, because filter 2 would make them blocking anyway and filter 1 must not get the chance to defer them first. If a reviewer flags something excluded and is *right* โ€” the exclusion was wrong โ€” fix the work and correct the brief. ## The materiality bar @@ -81,6 +81,8 @@ This is not a licence to ignore problems. It is a ranking rule: report what clea Materiality is the **last** of three, and applying it out of order produces the wrong verdict: 1. **Scope** โ€” is this finding ours at all? An out-of-scope finding is deferred with the exclusion that covers it, **however material it looks in isolation**. A valid improvement to work this change deliberately did not do is still out of scope. Materiality never promotes something back into scope. + + **"The reviewer was right" does not by itself re-open scope.** An excluded observation can be perfectly true โ€” for a docs-only change, "the implementation is absent" is *correct* and still out of scope, because its absence is the exclusion. Only two things pull an item back in: it is a defect in work this change **actually did**, or the exclusion itself was invalid (it excluded something a brief may not exclude, such as a security, privacy, data-loss or correctness problem inside the change). Anything else is deferred with the exclusion cited, no matter how accurate. 2. **Contract** โ€” is it a violation of a project rule, security or privacy invariant, or another mandatory requirement the project wrote down? Those are **blocking by virtue of being rules**, and the bar does not filter them. The project already decided they matter; that decision is not a reviewer's to re-make per finding. 3. **Materiality** โ€” for everything left, which is findings a reviewer originates from its own judgment: does acting on it change anything? diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index 7452a18..e2f30e9 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -32,7 +32,7 @@ Then review within it: exclusion that covers it** โ€” never raised as blocking, never silently dropped. - **The brief never suppresses a real finding.** It excludes work deliberately not done; it does not excuse defects in the work that *was* done. Security, - data-loss, and correctness problems inside the change are always blocking, + **privacy**, data-loss, and correctness problems inside the change are always blocking, whatever the brief says. If an excluded finding turns out to be correct, the exclusion was wrong โ€” say so plainly. - Judge the change against **what was asked for**, not against what you would @@ -125,8 +125,8 @@ For projects with vendor submodules (e.g., `vendor/` directory): 3. **Code review**: bugs, security, performance ## Standards -- APPROVE minor issues -- BLOCK: security, bugs, **vendor reuse violations** +- BLOCK: every **blocking** finding โ€” contract blockers (project rules, security, privacy, **vendor reuse violations**), Material, and Substantive. The canonical definition is in `fx-dev/skills/dev/references/scope-contract.md`; do not narrow it here. +- APPROVE despite immaterial observations โ€” they belong in the closing note, never in the decision - Ship good code, not perfect ## Output Format diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index b287be8..28274f2 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -17,7 +17,7 @@ Pass it into every resolver you invoke, and classify each finding before acting: - **Covered by the brief's out-of-scope list** โ†’ resolve the thread as deferred, citing the exclusion. Never silently fix it, never silently drop it, and never widen the PR to satisfy it. - **Excluded but correct** โ†’ the exclusion was wrong. Fix the work and say the brief was wrong. -Security, data-loss, and correctness problems **inside** the change are always in scope, whatever the brief says. Resolving out-of-scope suggestions is scope creep with a reviewer's name on it. +Security, **privacy**, data-loss, and correctness problems **inside** the change are always in scope, whatever the brief says. Resolving out-of-scope suggestions is scope creep with a reviewer's name on it. ## Also triage by materiality @@ -175,6 +175,19 @@ Codecov feedback exists if: ### 4. Invoke Appropriate Resolver Skills +**Triage BEFORE dispatching, and pass the result.** A resolver invoked with a bare +skill name has neither the Scope Brief nor your per-finding dispositions, so it +re-derives both โ€” and a correct-but-immaterial thread comes back as an edit, +which is the push that reopens the loop. Every invocation below MUST carry, as +its argument: + +1. The **Scope Brief**, verbatim (`fx-dev/skills/dev/references/scope-contract.md` ยง The Scope Brief). +2. A **disposition per thread**: `blocking` (fix and push), `immaterial` + (reply with the reasoning and resolve โ€” no edit), or `deferred` (reply citing + the exclusion and resolve โ€” no edit). + +`skill="fx-dev:copilot-feedback-resolver", args=" โ€” dispositions: blocking, immaterial, โ€ฆ"` + **If Copilot threads exist:** ``` Skill tool: skill="fx-dev:copilot-feedback-resolver" @@ -203,7 +216,7 @@ After invoking resolver skills, re-query to confirm all threads are resolved AND 3. Re-query unresolved threads (per below). 4. If the breakdown array is non-empty, re-invoke the relevant resolver(s). 5. After fixes are pushed, restart at step 1 โ€” the push created unreviewed commits. -6. Stop when a pass produces **no new blocking findings** on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 15 outer iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if not converged โ€” reaching the bound is a failure to converge, reported as such, not a pass. Escalate earlier on a repeated disagreement rather than spending the remaining iterations; and when the blocking count rises **with every new finding in one class**, fix that cause first โ€” a count rising across different categories means the review is still productive, not diverging. +6. Stop when **no blocking finding is unresolved** โ€” not merely when the newest pass produced no *new* one. A blocker carried from an earlier pass still blocks even if this pass did not repeat it, and a suppressed Copilot item creates no thread, so neither a zero-thread count nor a quiet pass discharges it. Track them and clear the list. That state must hold on a head SHA that was actually reviewed (verify: the newest Copilot review's `commit_id` equals `headRefOid`), **every automated thread on that head is resolved**, and **the suppressed-comments block for that head is empty or fully triaged** (see ยง3 *Identify Unresolved Feedback by Source*, not this loop's step 3 โ€” those findings create no thread, so a zero-thread count says nothing about them). Immaterial findings resolved by reply satisfy this โ€” they are not "new feedback" for the purpose of another cycle, because actioning them would produce a push and therefore manufacture the next round's input. Cap at 15 outer iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if not converged โ€” reaching the bound is a failure to converge, reported as such, not a pass. Escalate earlier on a repeated disagreement rather than spending the remaining iterations; and when the blocking count rises **with every new finding in one class**, fix that cause first โ€” a count rising across different categories means the review is still productive, not diverging. **โ›” Zero new threads is not convergence unless a Copilot review has been RECEIVED for the current head SHA.** "Received for the current head" is the *only* condition โ€” do **not** phrase it as "was requested", and do not try to verify that a request happened: `requested_reviewers` is empirically always empty, so whether a review was requested is not a determinable fact (see `fx-dev:copilot-review` **D1**/**D3**). Issue the nudge because it sometimes helps, then judge convergence solely on the delivered review. Absence of feedback is not evidence of quality. @@ -287,7 +300,7 @@ If unresolved threads remain, report which reviewers still have open feedback. 1. All unresolved automated review threads identified โ€” matched on the `copilot-pull-request-reviewer` login, **not** the bare `Copilot` โ€” **plus** any findings in the "Suppressed comments" block of every Copilot review **of the current head commit**, which produce no threads 2. Appropriate resolver skill(s) invoked (Copilot + CodeRabbit in parallel where applicable) -3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, produced no new blocking findings, and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence +3. The wait-and-resolve loop has CONVERGED โ€” a Copilot review has been **RECEIVED for the current head SHA**, left **no blocking finding unresolved** โ€” including any carried from an earlier pass and any suppressed item, which creates no thread and so is never discharged by a thread count โ€” and every automated thread on that head is resolved. Immaterial findings resolved by reply do not block this. Do not add "and was requested", which is not a determinable fact (**D1**). A quiet poll on an unreviewed head is not convergence 4. CodeRabbit's check is in a terminal passing state (or absent if not configured) 5. Final verification confirms all threads resolved 6. Summary output provided From fc1b5ddd8513eda0f0c5643dc6d828974375188a Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 19:25:34 +0000 Subject: [PATCH 19/48] fix(fx-dev): define normative rules once, reference them everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 7 returned 6 findings. Trend: 4, 4, 8, 2, 3, 8, 6 blocking. Nearly all of them were one shape, and it is the shape passes 5, 6 and 7 have now produced in a row: a canonical rule gets fixed, its downstream paraphrases do not, and the stale copy is always the narrower one โ€” so it under-blocks, under-converges, or re-opens scope it should not. Patching the instances has not worked three times running, so this commit addresses the cause. scope-contract.md now generalises the rule that was already stated for the blocking term: every normative rule here is defined once and referenced, never restated. That covers the blocking definition, the convergence test, the iteration bound, the two conditions that re-open scope, and the resolver dispositions. A skill needing different behaviour changes it there, for everyone. Then the six instances, swept to completion and verified with unfiltered greps this time: - Convergence said "no new blocking findings" in five places against a canonical rule requiring none *unresolved*, so a blocker carried from an earlier pass โ€” or a suppressed Copilot item, which creates no thread โ€” could survive a quiet pass. All five now test unresolved, and say why. - Seven copies of "if the reviewer was right, the exclusion was wrong" contradicted the rule added last commit, under which being correct is explicitly not what re-opens scope: "the implementation is absent" is true of a docs-only change and stays deferred. All seven now point at the two conditions that do re-open it. - pr-reviewer's Standards line re-enumerated contract blocker, Material and Substantive one commit after the contract forbade exactly that. It points at the definition now, noting only that vendor reuse violations are a project rule and therefore already blocking under it. - resolve-pr-feedback showed the required Scope-Brief-and-dispositions payload in a detached example while the three executable-looking dispatch blocks stayed bare. All three carry it, and a bare invocation is now named as an incomplete call rather than a shorthand. - copilot-feedback-resolver's table gained a valid-immaterial row last commit, but the detailed "Valid Concerns" section below still delegated every valid concern to a coder and required a push. Split to match the table. - Reaching the iteration bound advanced to PR creation when only deferred entries remained, which made the bound a successful exit โ€” the precise reading the previous commit set out to remove. Both sites now stop, report the trend, and hand the decision to the user. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 8 +++++--- plugins/fx-dev/skills/codex-review/SKILL.md | 10 ++++++---- .../skills/copilot-feedback-resolver/SKILL.md | 19 ++++++++++++++++++- plugins/fx-dev/skills/copilot-review/SKILL.md | 2 +- plugins/fx-dev/skills/dev/SKILL.md | 10 +++++----- .../skills/dev/references/scope-contract.md | 8 +++++--- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 8 +++++--- .../skills/resolve-pr-feedback/SKILL.md | 17 +++++++++++------ 10 files changed, 58 insertions(+), 28 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 02ad736..636bea8 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.24.2", + "version": "0.24.3", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 708b838..0f085a8 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": "3.11.2", + "version": "3.11.3", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 9ea96fd..f787a72 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -70,8 +70,10 @@ coordinator's ledger. **The brief never suppresses a real finding.** It excludes work deliberately not done; it does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always in scope. If a -finding the brief excluded turns out to be correct, the exclusion was wrong โ€” fix -the work and correct the brief. +finding the brief excluded meets one of the two conditions in +`fx-dev/skills/dev/references/scope-contract.md` ยง Three filters, filter 1, the +exclusion was invalid โ€” fix the work and correct the brief. Being merely correct +is not one of them. Persistent out-of-scope noise across passes means the brief is too thin. Tighten it rather than filtering the same findings by hand every round. @@ -108,7 +110,7 @@ Treat findings like self-review feedback: ### Step 3: Re-run until it converges (REQUIRED) -Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until a pass produces no blocking findings.** +Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until no blocking finding is left unresolved** โ€” including any carried from an earlier pass, not merely none new this pass. **Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 367f56a..b1834b4 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -86,8 +86,10 @@ an incomplete pass; rerun it with one rather than filtering the output by hand. **Do not use the brief to silence real findings.** It excludes work deliberately not done โ€” it does not excuse defects in the work that *was* done. If Codex -flags an "excluded" item and turns out to be right, the exclusion was wrong: fix -the work and correct the brief. +flags an "excluded" item and one of the two conditions in +`fx-dev/skills/dev/references/scope-contract.md` ยง Three filters, filter 1 holds, +the exclusion was invalid: fix the work and correct the brief. Being merely right +is not one of them. ## How to Run (one-shot, branch vs main) @@ -285,7 +287,7 @@ than waiting it out. Run the review again after fixes, **carrying the same Scope Brief prompt plus anything newly established**. Note the iteration number in the prompt and add facts verified since the last pass, so Codex does not relitigate settled ground. -**Repeat Steps 1 โ†’ 2 until a pass produces no blocking findings** +**Repeat Steps 1 โ†’ 2 until no blocking finding is left unresolved** โ€” including any carried from an earlier pass, not merely none new this pass โ€” โ€” per the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. **Converged does NOT mean zero output.** Codex will keep producing immaterial @@ -352,7 +354,7 @@ If the artifact is internally consistent and matches the tree, say so plainly. If Codex reports something the Scope Brief excluded, do NOT fix it and do NOT quietly drop it. Record it as deferred, with the exclusion that covers it. The -one exception: if the finding is *correct* and the exclusion was wrong, fix the +one exception: if the exclusion was *invalid* under ยง Three filters, filter 1, fix the work and correct the brief โ€” then say that the brief was wrong. A run that produces **zero** out-of-scope findings is the signal the brief was diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index 48cf934..ce6500b 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -231,7 +231,12 @@ mutation { - Example: "- Do not suggest removing `.sr-only` classes - required accessibility utilities" - **Never create or edit `.github/copilot-instructions.md`** โ€” obsolete; Copilot reads `REVIEW.md` directly -#### Valid Concerns +#### Valid โ€” blocking + +Only for concerns that clear the bar (`fx-dev/skills/dev/references/scope-contract.md` +ยง Blocking): a project-rule, security or privacy violation, or a Material or +Substantive finding. + 1. Delegate to coder sub-agent with: - PR number and title - File and line number @@ -239,6 +244,18 @@ mutation { - Thread ID for resolution after fix 2. Ensure coder pushes changes and resolves thread +#### Valid โ€” immaterial + +Correct, but would change nothing if it shipped uncorrected: wording, formatting, +a count nothing keys on, an entry missing from a list the artifact declares +non-exhaustive. + +1. **Do not delegate and do not edit.** A fix push reopens the review loop for an + item that changes nothing, and Copilot must then re-review the new head. +2. Reply with the reasoning โ€” what the observation is, and why it is below the + bar โ€” and resolve the thread. The gate is zero *unresolved* threads, not zero + observations acted on. + #### Deferred (Out of Scope) **When feedback is valid but out of scope for the current PR:** diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index 9a100d2..ddd3d7b 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -300,7 +300,7 @@ Resolving feedback usually means pushing commits. Those commits are **unreviewed If the head SHA changed since the review in Step 2, go back to **Step 1** โ€” nudge, wait (Step 2), read suppressed comments (Step 2b), resolve. Cap at 15 iterations (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) and escalate to the user if it has not settled. The bound is a backstop, not a budget: escalate as soon as the same disagreement repeats, and when the blocking count rises **with every new finding in one class**, fix that cause before escalating โ€” a count that rises across different categories means the review is still productive. Every iteration here costs a full Copilot wait cycle. -**Convergence is: no new blocking findings, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only blocking findings should produce one. +**Convergence is: no blocking finding left unresolved, every thread resolved, and the suppressed block empty-or-triaged โ€” all on a reviewed head.** *Unresolved*, not *new*: a blocker carried from an earlier pass still blocks even if this pass did not repeat it, and a suppressed item creates no thread, so no thread count discharges it. It is NOT "zero new threads". Resolving an immaterial thread by reply creates no commit, so the head does not move and no further pass is owed; nudging for another review to chase a zero-thread pass spends a wait cycle to change nothing. **Only a push restarts this loop**, which is why only blocking findings should produce one. ```bash # The gate is only passed when the newest Copilot review covers the current head. diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index c669168..4850724 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -274,7 +274,7 @@ git diff main --stat **MANDATORY: Run one complete local review matrix before creating the PR.** Run each available pass once in order against the current `HEAD`, record the revision that each channel reviewed, and classify its findings before accepting fixes. If `/simplify` edits directly, retain only changes that satisfy the contract classification and record the resulting revision before starting the next pass. -**โ›” EVERY pass below MUST receive the Step 2.5 Scope Brief verbatim.** A reviewer handed a bare diff reports the work you deliberately did not do โ€” missing implementation for a docs-only change, missing tests for a spec, dependencies a later phase adds โ€” and each such finding costs a full cycle to filter by hand. A pass run without the brief is incomplete: rerun it with the brief rather than filtering its output. Findings the brief excludes are recorded as deferred with the covering exclusion, never silently fixed and never silently dropped โ€” unless the finding is correct and the exclusion was wrong, in which case fix the work and correct the brief. +**โ›” EVERY pass below MUST receive the Step 2.5 Scope Brief verbatim.** A reviewer handed a bare diff reports the work you deliberately did not do โ€” missing implementation for a docs-only change, missing tests for a spec, dependencies a later phase adds โ€” and each such finding costs a full cycle to filter by hand. A pass run without the brief is incomplete: rerun it with the brief rather than filtering its output. Findings the brief excludes are recorded as deferred with the covering exclusion, never silently fixed and never silently dropped โ€” unless one of the two conditions in `references/scope-contract.md` ยง Three filters, filter 1 re-opens scope, in which case fix the work and correct the brief. A finding being merely correct is not one of them. **1. `/simplify`** โ€” reuse, quality, efficiency cleanup: @@ -329,9 +329,9 @@ Proceed to Step 5 when all of the following are true: **`follow-up/out-of-scope` with a Material or Substantive tier is not a legal combination.** The two fields are assigned by different filters and cannot disagree: an out-of-scope finding is deferred at filter 1 and never reaches the bar, so its tier is `n/a`; a finding that *does* reach the bar is in scope by construction, so if it ranks Material or Substantive it is a defect in work this change actually did, and belongs under `required-by-contract` or `regression-caused-by-change`. If you are about to record that pair, one of the two filters was misapplied โ€” re-run them rather than writing an entry the gate can neither clear nor waive. Materiality never promotes an out-of-scope finding back into scope (`references/scope-contract.md` ยง Three filters); this rule is that principle applied to the ledger. 2. Contract-required tests and tests affected by the latest delta pass. 3. Every available review channel completed its initial pass or has a documented permitted degradation. -4. One verification pass over the latest affected delta produces no new blocking finding. +4. One verification pass over the latest affected delta leaves no blocking finding unresolved โ€” including any carried from an earlier pass. -`follow-up/out-of-scope` entries and non-contract suggestions do not block PR creation. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. At the bound, create the PR only if every remaining entry is `follow-up/out-of-scope` with tier `n/a`. If a blocking entry remains, STOP and report it to the user โ€” the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. +`follow-up/out-of-scope` entries and non-contract suggestions do not block PR creation. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. Reaching the bound is a failure to converge and does not authorize Step 5. STOP, report the per-pass trend and everything still open, and let the user decide whether to create the PR โ€” including when every remaining entry is `follow-up/out-of-scope` with tier `n/a`. A blocking entry at the bound is always an escalation; the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. **Do not spend the headroom.** The bound is far above what a healthy channel needs; the exits that should actually end a loop โ€” converged, the same disagreement twice, a rising blocking count of one class โ€” all fire in single digits. A round that resolves only immaterial items is churn at any iteration number, and the convergence rule already forbids it. @@ -647,13 +647,13 @@ Concrete recipe: 2. In the foreground, wait for Copilot using the bundled `copilot-review` waiter, then read its unresolved threads without invoking a resolver. Classify and deduplicate them in the coordinator-owned ledger. 3. Wait for the background CodeRabbit waiter to finish, then read its unresolved threads and classify them before invoking a resolver. Invoke each reviewer resolver only after classification, passing the blocking findings and instructing it to settle deferred findings without code or task-tracker changes. 4. After a resolver pushes, record the new SHA and inspect only feedback added or changed since the previous reviewed SHA. Classify and deduplicate it in the shared ledger. Rerun only the reviewer whose state or evidence the delta invalidated; do not restart every reviewer merely because `HEAD` changed. -5. Stop after one latest-delta pass produces no new `required-by-contract` or `regression-caused-by-change` finding and every required reviewer thread is settled. +5. Stop after one latest-delta pass leaves no blocking finding unresolved โ€” including any carried from an earlier pass โ€” and every required reviewer thread is settled. Blocking is defined in `references/scope-contract.md` ยง Blocking. If `Bash` `run_in_background` isn't available in your context, fall back to fully-serial: Copilot first, then CodeRabbit. Slower but correct. ##### Bounded delta review (both modes) -Fix only blocking-class findings. Record follow-up/out-of-scope feedback without implementing it, and settle its thread with an out-of-scope disposition when repository policy permits. Each reviewer's remediation loop caps at the canonical bound in `references/scope-contract.md` ยง The iteration bound โ€” 15 rounds, the single number no local instruction overrides. Convergence, not the bound, is what should end it: the early exits (converged, the same disagreement twice, a rising blocking count of one class) all fire in single digits. At the bound, continue when only follow-up/out-of-scope findings remain; escalate any unresolved blocking-class finding to the user. Do not seek zero suggestions or restart unrelated review channels. +Fix only blocking-class findings. Record follow-up/out-of-scope feedback without implementing it, and settle its thread with an out-of-scope disposition when repository policy permits. Each reviewer's remediation loop caps at the canonical bound in `references/scope-contract.md` ยง The iteration bound โ€” 15 rounds, the single number no local instruction overrides. Convergence, not the bound, is what should end it: the early exits (converged, the same disagreement twice, a rising blocking count of one class) all fire in single digits. Reaching the bound is a failure to converge, not an exit you may take: STOP, report the per-pass trend and what remains, and hand the decision to the user. Do not advance to the next workflow step on the strength of having hit it, even when only follow-up/out-of-scope entries remain. Do not seek zero suggestions or restart unrelated review channels. ##### Skip rules diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 3168345..6f8471b 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -68,7 +68,7 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i **A reviewer invoked without a brief MUST reconstruct one** from the conversation before reviewing, and say that it did. Reviewing a diff with no idea what was asked for is the failure mode this contract exists to prevent โ€” never proceed as if the diff speaks for itself. -**Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always in scope, whatever the brief says โ€” a brief cannot exclude them, because filter 2 would make them blocking anyway and filter 1 must not get the chance to defer them first. If a reviewer flags something excluded and is *right* โ€” the exclusion was wrong โ€” fix the work and correct the brief. +**Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always in scope, whatever the brief says โ€” a brief cannot exclude them, because filter 2 would make them blocking anyway and filter 1 must not get the chance to defer them first. If a reviewer flags something excluded and the exclusion turns out to be invalid โ€” see ยง Three filters, filter 1, for the only two things that re-open scope โ€” fix the work and correct the brief. Being *correct* is not one of them: a true observation about work the change deliberately did not do stays deferred. ## The materiality bar @@ -98,7 +98,9 @@ A finding that fails filter 1 is deferred. One that passes filter 2 is a **contr **An immaterial finding is never blocking.** That is the whole vocabulary: *blocking* or *immaterial*. -Every downstream rule is written in terms of **blocking**, deliberately. Fix blocking findings; push for blocking findings; keep going while blocking findings arrive; converge when a pass produces none. Skills MUST NOT re-enumerate the three constituents in their own stopping rules โ€” an enumeration copied to a dozen sites silently goes stale the moment this list changes, and every copy then under-blocks. Say "blocking" and point here. +Every downstream rule is written in terms of **blocking**, deliberately. Fix blocking findings; push for blocking findings; keep going while blocking findings arrive; converge when none is left unresolved. + +**This generalises, and it is the rule that keeps this document workable: every normative rule here is defined once and referenced, never restated.** That covers the blocking definition, the convergence test, the iteration bound, the two conditions that re-open scope, and the resolver dispositions. A skill that paraphrases one of them creates a copy that goes stale the moment the original changes โ€” and a stale copy is always the *narrower* one, so it under-blocks, under-converges, or re-opens scope it should not. Name the rule and link the section. If a skill genuinely needs different behaviour, change it here, for everyone. ### The bar @@ -129,7 +131,7 @@ When unsure which tier something is, ask: *if this shipped uncorrected, what bre Track findings per pass and watch the shape, not just the count: - **Blocking findings still arriving, in new categories** โ€” keep going. The review is still working. -- **No blocking findings this pass** โ€” converged, whatever the immaterial count did. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. +- **No blocking finding left unresolved** โ€” converged, whatever the immaterial count did. Note the test is *unresolved*, not *new*: a blocker carried from an earlier pass still blocks even if this pass did not repeat it, and a finding that produces no thread (a suppressed Copilot item) is never discharged by a thread count. Track them and clear the list. Stop, and say so: report the trend and what remains below the bar. A pass that falls from five immaterial observations to three *different* immaterial observations has converged; the drop is churn, not progress. - **The same disagreement in successive passes** โ€” stop. That is a human decision, not a review outcome. Escalate it by name. Report the trend when you stop, so the operator can see the shape rather than take "clean" on trust: *"Findings per pass: 9, 4, 1, 0 blocking. Stopping โ€” three immaterial wording items remain, listed below."* diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index e2f30e9..94ac91d 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -33,8 +33,10 @@ Then review within it: - **The brief never suppresses a real finding.** It excludes work deliberately not done; it does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always blocking, - whatever the brief says. If an excluded finding turns out to be correct, the - exclusion was wrong โ€” say so plainly. + whatever the brief says. An excluded finding re-enters scope only on the two + conditions in `fx-dev/skills/dev/references/scope-contract.md` ยง Three filters, + filter 1 โ€” being merely correct is not one of them. When one of them holds, say + so plainly. - Judge the change against **what was asked for**, not against what you would have built. "This should also handle X" is out of scope unless the request, the spec, or a genuine regression demands it. @@ -125,7 +127,7 @@ For projects with vendor submodules (e.g., `vendor/` directory): 3. **Code review**: bugs, security, performance ## Standards -- BLOCK: every **blocking** finding โ€” contract blockers (project rules, security, privacy, **vendor reuse violations**), Material, and Substantive. The canonical definition is in `fx-dev/skills/dev/references/scope-contract.md`; do not narrow it here. +- BLOCK: every **blocking** finding, as defined in `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking. Do not restate or narrow that definition here โ€” **vendor reuse violations** are a project rule and therefore blocking under it. - APPROVE despite immaterial observations โ€” they belong in the closing note, never in the decision - Ship good code, not perfect diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 28274f2..ca11856 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -15,7 +15,7 @@ Pass it into every resolver you invoke, and classify each finding before acting: - **In scope** โ†’ resolve it. - **Covered by the brief's out-of-scope list** โ†’ resolve the thread as deferred, citing the exclusion. Never silently fix it, never silently drop it, and never widen the PR to satisfy it. -- **Excluded but correct** โ†’ the exclusion was wrong. Fix the work and say the brief was wrong. +- **Excluded, and the exclusion was invalid** โ†’ per `fx-dev/skills/dev/references/scope-contract.md` ยง Three filters, filter 1, only two things re-open scope: it is a defect in work this change actually did, or the exclusion covered something a brief may not exclude. Being merely correct is not one of them. When one holds, fix the work and say the brief was wrong. Security, **privacy**, data-loss, and correctness problems **inside** the change are always in scope, whatever the brief says. Resolving out-of-scope suggestions is scope creep with a reviewer's name on it. @@ -186,23 +186,28 @@ its argument: (reply with the reasoning and resolve โ€” no edit), or `deferred` (reply citing the exclusion and resolve โ€” no edit). -`skill="fx-dev:copilot-feedback-resolver", args=" โ€” dispositions: blocking, immaterial, โ€ฆ"` - **If Copilot threads exist:** ``` -Skill tool: skill="fx-dev:copilot-feedback-resolver" +Skill tool: skill="fx-dev:copilot-feedback-resolver", + args=" โ€” dispositions: blocking, immaterial, deferred ()" ``` **If CodeRabbit threads exist:** ``` -Skill tool: skill="fx-dev:rabbit-feedback-resolver" +Skill tool: skill="fx-dev:rabbit-feedback-resolver", + args=" โ€” dispositions: blocking, immaterial, deferred ()" ``` **If Codecov coverage gaps detected:** ``` -Skill tool: skill="fx-dev:resolve-codecov-feedback" +Skill tool: skill="fx-dev:resolve-codecov-feedback", + args=" โ€” uncovered lines in scope: ; deliberately uncovered: " ``` +**No bare invocation.** A `Skill tool:` line with no `args` is an incomplete call +here, not a shorthand โ€” the resolver then re-derives triage it cannot see and +edits for findings you classified immaterial or deferred. + **If multiple exist:** Prefer running Copilot and CodeRabbit resolvers **in parallel** by spawning each as a sub-agent in the same message (see `fx-dev:dev` Step 6.3 for the exact pattern). Codecov is sequential after them since coverage fixes typically require code from the other resolvers to be in place first. ### 5. Verify All Resolved AND Loop Until Convergence From 27d551b5f3ea865886c59b2f04a2fee7c540ffe7 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 19:43:36 +0000 Subject: [PATCH 20/48] fix(fx-dev): make the define-once rule true, and honour dispositions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 8 returned 3 findings. Trend: 4, 4, 8, 2, 3, 8, 6, 3 blocking. The sharpest one is that the define-once rule added last commit was not actually satisfied by the commit that added it โ€” and one of the violations it named is a case the rule was wrong to forbid. A prompt handed to `codex review` crosses a process boundary: Codex receives a string and cannot follow a link, so that block must inline the definition. The rule now carves out exactly that case, and only that case โ€” text sent verbatim to an external tool. Such a block is a **mirror**: marked as one, kept a faithful restatement, updated in the same commit as the canonical text. The injected prompt now carries that marker. Nothing an agent reads directly qualifies. The genuine violations are fixed. "Resolver dispositions" was named canonical while actually being defined in resolve-pr-feedback, so ยง Resolver dispositions now exists in the contract and resolve-pr-feedback references it. Its reopen-scope bullet had restated both conditions rather than pointing at them. Six exits still said convergence was "a pass produces none" โ€” including the iteration bound's own list of exits, which called that the only successful one. Against a canonical rule that tests the ledger rather than the latest pass, that let a carried or suppressed blocker sit open behind a quiet pass. All six now say "none left unresolved" and point at ยง Convergence. Last, copilot-feedback-resolver categorised every thread from its own table even when the coordinator had supplied authoritative dispositions. A blocker carrying a `[nitpick]` prefix was auto-resolved by the first row, and a `deferred` thread could still take the mandatory tracker-update path and commit to the implementation PR โ€” suppressing a blocker and widening the change respectively. Coordinator dispositions now explicitly win, because they are set with the Scope Brief and ledger in hand and the table classifies from comment text alone. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 6 +++--- plugins/fx-dev/skills/codex-review/SKILL.md | 11 +++++++++-- .../skills/copilot-feedback-resolver/SKILL.md | 11 ++++++++++- .../skills/dev/references/scope-contract.md | 16 ++++++++++++++-- .../fx-dev/skills/resolve-pr-feedback/SKILL.md | 9 +++++---- 7 files changed, 43 insertions(+), 14 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 636bea8..7493709 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.24.3", + "version": "0.24.4", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 0f085a8..2425793 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": "3.11.3", + "version": "3.11.4", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index f787a72..65f9518 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -13,8 +13,8 @@ CodeRabbit reviews code with AI. The **primary** way to use it is **locally, via Catch CodeRabbit's feedback **before** a PR exists, using the `cr` CLI on your local changes: -- Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix every **blocking** finding, and re-run until a pass produces none. Immaterial observations get one closing note and do not buy another run โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. -- Open the PR once the local review has **converged** โ€” no blocking findings โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve the blocking findings already received before proceeding; immaterial ones travel as a closing note. +- Run `cr` during pre-PR self-review (alongside `/simplify` and `/review`), fix every **blocking** finding, and re-run until none is left unresolved (`fx-dev/skills/dev/references/scope-contract.md` ยง Convergence โ€” the ledger test, not "the latest pass was quiet"). Immaterial observations get one closing note and do not buy another run โ€” see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. +- Open the PR once the local review has **converged** โ€” no blocking finding left unresolved โ€” **or is correctly degraded as `skipped (rate-limited)`**. Resolve the blocking findings already received before proceeding; immaterial ones travel as a closing note. - A clean local review does NOT remove the merge gates โ€” but it usually means CodeRabbit's PR-level review (when the GitHub App is configured) lands clean on the first pass, and often there is nothing left to resolve on the PR at all. ## The `cr` CLI @@ -121,7 +121,7 @@ Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until no block ### Step 4: Open the PR when clean or correctly degraded -A **converged** local CodeRabbit review โ€” no blocking findings โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once every blocking finding already received is resolved โ€” the degradation waives the *unrun* remainder of the review, never a finding it already delivered. Do not open the PR with known unresolved blocking findings; immaterial observations travel as a closing note in the PR description. +A **converged** local CodeRabbit review โ€” no blocking finding left unresolved, per `fx-dev/skills/dev/references/scope-contract.md` ยง Convergence โ€” is preferred before PR creation. A rate-limited review is correctly degraded and does not block PR creation once every blocking finding already received is resolved โ€” the degradation waives the *unrun* remainder of the review, never a finding it already delivered. Do not open the PR with known unresolved blocking findings; immaterial observations travel as a closing note in the PR description. --- diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index b1834b4..67915ca 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -8,7 +8,8 @@ description: Run OpenAI Codex's AI code review LOCALLY via the `codex` CLI BEFOR This skill runs OpenAI Codex's AI code review **locally, one-shot**, against the current branch. Run it as part of pre-PR self-review โ€” after `coderabbit-review`, before opening the PR โ€” fix every **blocking** finding, and open -the PR once a pass produces none. Immaterial observations get one closing note +the PR once none is left unresolved (`fx-dev/skills/dev/references/scope-contract.md` +ยง Convergence โ€” the ledger test, not "the latest pass was quiet"). Immaterial observations get one closing note and do not hold the PR: see the materiality bar in `fx-dev/skills/dev/references/scope-contract.md`. @@ -299,6 +300,12 @@ and list those items once, non-blocking. pass where it pays. Every re-run prompt MUST carry, in addition to the Scope Brief: +> **This block is a MIRROR** of `fx-dev/skills/dev/references/scope-contract.md` +> ยง Blocking and ยง Three things that are not findings โ€” the one case the +> define-once rule exempts, because `codex review` receives a string and cannot +> follow a link. Keep it a faithful restatement, never an independent edit, and +> update it in the same commit that changes the canonical text. + ``` CONVERGENCE PASS . Prior passes found issues; all fixed except . Do not re-report them. @@ -363,7 +370,7 @@ it before the next iteration rather than filtering by hand again. ### Step 4: Open the PR once it has converged -A **converged** Codex review โ€” a pass with no blocking findings โ€” +A **converged** Codex review โ€” no blocking finding left unresolved, per `fx-dev/skills/dev/references/scope-contract.md` ยง Convergence โ€” alongside a converged CodeRabbit review is the gate to PR creation in the SDLC (`fx-dev:dev` Step 4.5 โ†’ Step 5). Do not open the PR with any unresolved blocking Codex finding. Outstanding **immaterial** observations do not hold diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index ce6500b..747454d 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -165,9 +165,18 @@ query { For each unresolved Copilot comment: +**If the coordinator supplied per-thread dispositions, they win.** A disposition +from `fx-dev:resolve-pr-feedback` is set with the Scope Brief and the finding +ledger in hand; this table classifies from the comment text alone. Apply the +disposition (`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver +dispositions) and use the table only for threads it did not cover. In particular +a thread marked `blocking` is fixed even if it carries a `[nitpick]` prefix, and +a thread marked `deferred` is replied to and resolved without editing anything โ€” +including without committing a tracker update to this PR. + | Category | Indicator | Action | |----------|-----------|--------| -| **Nitpick** | Contains `[nitpick]` prefix | Auto-resolve immediately | +| **Nitpick** | Contains `[nitpick]` prefix | Auto-resolve immediately โ€” **unless the coordinator marked it `blocking`** | | **Outdated** | Refers to code that no longer exists | Reply with explanation, resolve | | **Incorrect** | Misunderstands project conventions | Reply with explanation, resolve, update `REVIEW.md` | | **Valid โ€” blocking** | Current concern that clears the bar: a project-rule, security or privacy violation, or a Material or Substantive finding (`fx-dev/skills/dev/references/scope-contract.md`) | Delegate to coder sub-agent to fix | diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 6f8471b..9ea3aa1 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -100,7 +100,19 @@ A finding that fails filter 1 is deferred. One that passes filter 2 is a **contr Every downstream rule is written in terms of **blocking**, deliberately. Fix blocking findings; push for blocking findings; keep going while blocking findings arrive; converge when none is left unresolved. -**This generalises, and it is the rule that keeps this document workable: every normative rule here is defined once and referenced, never restated.** That covers the blocking definition, the convergence test, the iteration bound, the two conditions that re-open scope, and the resolver dispositions. A skill that paraphrases one of them creates a copy that goes stale the moment the original changes โ€” and a stale copy is always the *narrower* one, so it under-blocks, under-converges, or re-opens scope it should not. Name the rule and link the section. If a skill genuinely needs different behaviour, change it here, for everyone. +**This generalises, and it is the rule that keeps this document workable: every normative rule here is defined once and referenced, never restated.** That covers the blocking definition, the convergence test, the iteration bound, the two conditions that re-open scope, and the resolver dispositions below. A skill that paraphrases one of them creates a copy that goes stale the moment the original changes โ€” and a stale copy is always the *narrower* one, so it under-blocks, under-converges, or re-opens scope it should not. Name the rule and link the section. If a skill genuinely needs different behaviour, change it here, for everyone. + +**One exception, and only one: text sent verbatim to an external tool.** A prompt handed to `codex review`, `cr`, or any reviewer outside this repo cannot follow a link, so it MUST inline the rule. Such a block is a **mirror**: mark it as one, keep it a faithful restatement of the canonical section rather than an independent edit, and update it in the same commit that changes the canonical text. Nothing an agent reads directly qualifies โ€” only strings crossing a process boundary. + +### Resolver dispositions + +Every finding handed to a resolver carries exactly one, and they are defined here so no resolver invents a fourth: + +- **`blocking`** โ€” fix it and push. See ยง Blocking. +- **`immaterial`** โ€” reply with the reasoning and resolve. **No edit**: a fix push reopens the review loop for something that changes nothing. +- **`deferred`** โ€” reply citing the exclusion that covers it and resolve. **No edit**, and never widen the change to satisfy it. + +**A disposition assigned by the coordinator is authoritative and overrides a resolver's own categorisation.** The coordinator holds the Scope Brief and the ledger; a resolver classifying from the comment text alone does not, and its local heuristics โ€” a `[nitpick]` prefix, a tracker-update path โ€” must not override a disposition set with that context. ### The bar @@ -148,7 +160,7 @@ Report the trend when you stop, so the operator can see the shape rather than ta A loop should almost always end on one of the three signals above, all of which fire long before 15: -- **Converged** โ€” a pass with no blocking findings. The only successful exit. +- **Converged** โ€” no blocking finding left unresolved, per ยง Convergence above. The only successful exit. Note that is the *ledger* test, not a property of the latest pass: a quiet pass with a carried blocker still open is not convergence. - **The same disagreement twice** โ€” escalate by name. Do not spend the remaining iterations re-arguing it. - **A rising count of one class** โ€” the last fix is generating them. Fix the cause, or escalate the design choice. diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index ca11856..d84da1d 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -15,7 +15,7 @@ Pass it into every resolver you invoke, and classify each finding before acting: - **In scope** โ†’ resolve it. - **Covered by the brief's out-of-scope list** โ†’ resolve the thread as deferred, citing the exclusion. Never silently fix it, never silently drop it, and never widen the PR to satisfy it. -- **Excluded, and the exclusion was invalid** โ†’ per `fx-dev/skills/dev/references/scope-contract.md` ยง Three filters, filter 1, only two things re-open scope: it is a defect in work this change actually did, or the exclusion covered something a brief may not exclude. Being merely correct is not one of them. When one holds, fix the work and say the brief was wrong. +- **Excluded, and the exclusion was invalid** โ†’ only the two conditions in `fx-dev/skills/dev/references/scope-contract.md` ยง Three filters, filter 1 re-open scope. When one holds, fix the work and say the brief was wrong. Security, **privacy**, data-loss, and correctness problems **inside** the change are always in scope, whatever the brief says. Resolving out-of-scope suggestions is scope creep with a reviewer's name on it. @@ -182,9 +182,10 @@ which is the push that reopens the loop. Every invocation below MUST carry, as its argument: 1. The **Scope Brief**, verbatim (`fx-dev/skills/dev/references/scope-contract.md` ยง The Scope Brief). -2. A **disposition per thread**: `blocking` (fix and push), `immaterial` - (reply with the reasoning and resolve โ€” no edit), or `deferred` (reply citing - the exclusion and resolve โ€” no edit). +2. A **disposition per thread** โ€” `blocking`, `immaterial`, or `deferred`, as + defined in `fx-dev/skills/dev/references/scope-contract.md` ยง Resolver + dispositions. Yours is authoritative: it is set with the Scope Brief and the + ledger in hand, which the resolver does not have. **If Copilot threads exist:** ``` From 46413eef81dbe491cb84f6cea020c73155880709 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 19:54:19 +0000 Subject: [PATCH 21/48] fix(fx-dev): keep the mirror faithful, stop the nitpick bypass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 9 returned 3 findings. Trend: 4, 4, 8, 2, 3, 8, 6, 3, 3 blocking. The mirror had already drifted, one commit after being designated a mirror โ€” which is the failure mode the marker exists to catch, arriving immediately. Its contract-blocker bullet named AGENTS.md, REVIEW.md and security/privacy invariants, but the canonical filter also covers "another mandatory requirement the project wrote down": a change document, or a spec it links. A violation of one of those with no direct behavioural symptom fell through all three bullets and would not have been reported at all. The mirror now carries the full category. copilot-feedback-resolver auto-resolved any comment carrying Copilot's `[nitpick]` prefix. With a coordinator disposition present that was already overridden, but invoked directly โ€” or for a thread the coordinator did not cover โ€” a project-rule, security, privacy or correctness defect that Copilot happened to label a nitpick was closed without ever reaching the filters. The prefix is now an input to triage rather than a verdict, and the no-disposition path says to run scope โ†’ contract โ†’ materiality itself. Last, the always-in-scope carve-out claimed filter 2 would make all four categories blocking. Filter 2 is rules and invariants only; a correctness or data-loss defect a reviewer finds by reading the change is ranked at filter 3 and is Material there. Recording it as a contract blocker with tier `n/a` against the ledger rules was one legal reading and Material was another. The rule now says what it actually means: the brief cannot keep such a finding out of scope, and which filter catches it depends on its source โ€” both block. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 7 ++++--- plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md | 9 ++++++++- plugins/fx-dev/skills/dev/references/scope-contract.md | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 7493709..e6085f4 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.24.4", + "version": "0.24.5", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 2425793..10e43b8 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": "3.11.4", + "version": "3.11.5", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 67915ca..df2d42f 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -313,9 +313,10 @@ rejected ones and why>. Do not re-report them. Report every BLOCKING finding. A finding is blocking if it is any of: 1. A violation of a rule this project wrote down โ€” anything in AGENTS.md or - REVIEW.md, or a security or privacy invariant. Report these whatever their - direct behavioural impact; the project already decided they matter, so do not - weigh them against the bar below. + REVIEW.md, a security or privacy invariant, or any other mandatory + requirement the project recorded, including a change document or a spec it + links. Report these whatever their direct behavioural impact; the project + already decided they matter, so do not weigh them against the bar below. 2. Something that would change behaviour, break a build or test, make the artifact unimplementable, or expose a security, privacy, or data-loss problem โ€” including a leaked credential, internal URL, or private identifier in diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index 747454d..5d210c1 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -174,9 +174,16 @@ a thread marked `blocking` is fixed even if it carries a `[nitpick]` prefix, and a thread marked `deferred` is replied to and resolved without editing anything โ€” including without committing a tracker update to this PR. +**With no disposition โ€” this skill invoked directly, or a thread the coordinator +did not cover โ€” run the filters yourself: scope, then contract, then materiality +(`fx-dev/skills/dev/references/scope-contract.md` ยง Three filters).** Copilot's +`[nitpick]` prefix is an *input* to that judgment, never a verdict: it is the +reviewer's own label, and a project-rule, security, privacy or correctness defect +carrying it is still blocking. Never auto-resolve on the prefix alone. + | Category | Indicator | Action | |----------|-----------|--------| -| **Nitpick** | Contains `[nitpick]` prefix | Auto-resolve immediately โ€” **unless the coordinator marked it `blocking`** | +| **Nitpick** | Contains `[nitpick]` prefix **and clears none of the filters below** | Reply and resolve without editing | | **Outdated** | Refers to code that no longer exists | Reply with explanation, resolve | | **Incorrect** | Misunderstands project conventions | Reply with explanation, resolve, update `REVIEW.md` | | **Valid โ€” blocking** | Current concern that clears the bar: a project-rule, security or privacy violation, or a Material or Substantive finding (`fx-dev/skills/dev/references/scope-contract.md`) | Delegate to coder sub-agent to fix | diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 9ea3aa1..46a004d 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -68,7 +68,7 @@ State the **reason** each exclusion is deliberate. "Do not flag missing tests" i **A reviewer invoked without a brief MUST reconstruct one** from the conversation before reviewing, and say that it did. Reviewing a diff with no idea what was asked for is the failure mode this contract exists to prevent โ€” never proceed as if the diff speaks for itself. -**Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always in scope, whatever the brief says โ€” a brief cannot exclude them, because filter 2 would make them blocking anyway and filter 1 must not get the chance to defer them first. If a reviewer flags something excluded and the exclusion turns out to be invalid โ€” see ยง Three filters, filter 1, for the only two things that re-open scope โ€” fix the work and correct the brief. Being *correct* is not one of them: a true observation about work the change deliberately did not do stays deferred. +**Never use the brief to suppress real findings.** It excludes work that was deliberately not done. It does not excuse defects in the work that *was* done. Security, **privacy**, data-loss, and correctness problems inside the change are always in scope, whatever the brief says โ€” a brief cannot exclude them, and filter 1 must not get the chance to defer them before the later filters see them. Which filter then catches one depends on its source, and both outcomes block: a violation of a written rule or invariant is a contract blocker at filter 2, while a defect you found by reading the change is ranked at filter 3, where a real one is Material. The point of this rule is that the brief cannot keep such a finding out of scope โ€” not that every one of them is a contract blocker. If a reviewer flags something excluded and the exclusion turns out to be invalid โ€” see ยง Three filters, filter 1, for the only two things that re-open scope โ€” fix the work and correct the brief. Being *correct* is not one of them: a true observation about work the change deliberately did not do stays deferred. ## The materiality bar From 1b995768297c0fce9c682c003b7fd737a6c095f7 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 20:18:48 +0000 Subject: [PATCH 22/48] fix(fx-dev): align the detailed handlers with the bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 10 returned 5 findings, fixed in 7 edits. Trend: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5 blocking. One shape again, one level lower: the summary tables and triage paragraphs were brought to the bar in earlier commits, but the step-by-step handlers those tables point at still described pre-bar behaviour. A reader who follows the procedure rather than the summary got the old rules. - copilot-feedback-resolver's Nitpicks handler still defined the category by Copilot's `[nitpick]` prefix and resolved on sight. Its table row was fixed last commit; the handler was not, so the bypass survived one level down. It now runs the filters first and treats the prefix as a label. - The same skill's Deferred handler mandated adding and committing a PROJECT.md task โ€” an edit and a commit, for work the PR deliberately is not doing, which both widens the change and reopens the loop. Deferred is reply-and-resolve; the follow-up goes back to the coordinator, or is proposed to the user when the skill runs standalone. - dev's stopping condition waived "non-contract suggestions", which reads as permission to ship a reviewer-originated Material or Substantive finding โ€” a two-way ambiguity is exactly that and is blocking under item 1 directly above it. Only `follow-up/out-of-scope` with tier `n/a` and immaterial observations are waivable. - CodeRabbit's Mode 2 handed `rabbit-feedback-resolver` a bare PR number, so the dispositions and Scope Brief never reached the resolver actually acting. It now passes both, matching the Copilot path. - Both codex-review and coderabbit-review still said nitpicks "may be applied or consciously skipped". Applying one produces a commit, and Step 3 reruns against it, which manufactures the next pass to change something that changes nothing. Nitpicks are immaterial by definition and go in the closing note; one that turns out to clear the bar was never a nitpick. - The Codex mirror omitted the third canonical non-finding, so an artifact that admits and gates a limit could be reported as a missing step and force another pass. Added, keeping the mirror faithful. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 9 ++++++- plugins/fx-dev/skills/codex-review/SKILL.md | 12 +++++++-- .../skills/copilot-feedback-resolver/SKILL.md | 26 ++++++++++++++----- plugins/fx-dev/skills/dev/SKILL.md | 2 +- 6 files changed, 41 insertions(+), 12 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index e6085f4..a1b7d64 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.24.5", + "version": "0.24.6", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 10e43b8..3cc4fb2 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": "3.11.5", + "version": "3.11.6", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 65f9518..6cd7970 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -104,7 +104,7 @@ Treat findings like self-review feedback: - **Triage in the contract's order โ€” scope, then contract, then materiality** (`fx-dev/skills/dev/references/scope-contract.md`). An out-of-scope finding is deferred however material it looks, per the triage rules above; project rules and security/privacy invariants block regardless of the bar; only what remains is ranked. Blocking findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. - **Fix real issues** in code and tests; make atomic commits for the fixes. -- **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the project doesn't care about. +- **Nitpicks are immaterial by definition, so do not apply them.** They go in the closing note. Applying one produces a commit, and Step 3 then reruns against it โ€” manufacturing the next pass to change something that changes nothing. If a nitpick turns out to clear the bar, it was never a nitpick: fix it as the blocking finding it is. - **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the tree; when it does not hold, reject the finding with the evidence rather than changing working code to satisfy a misreading. - There are no PR threads to resolve here โ€” this is local. Resolution = the code is fixed (or the finding is a deliberate non-issue). @@ -191,6 +191,13 @@ If this count is 0 AND the CodeRabbit check is `success`, the gate is PASSED. **Cap the loop at 15 iterations** (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) โ€” if CodeRabbit is still posting new blocking feedback at the bound, escalate to the user and say the loop did not converge. Almost always a loop that runs that long means CodeRabbit and the codebase disagree on a design decision that needs human input โ€” which the same-disagreement-twice rule should have caught far earlier. Escalate when you see it, not at iteration 15. +**Hand the resolver the brief and a disposition per thread.** Invoking +`fx-dev:rabbit-feedback-resolver` with only a PR number makes it re-derive triage +it cannot see, and it will edit for threads you classified immaterial or +deferred. Pass `args=" โ€” โ€” dispositions: blocking, immaterial, deferred ()"`, per +`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver dispositions. + **Threads must all be resolved, but resolution is not the same as a fix.** An immaterial thread is resolved by replying with the reason it is not being actioned โ€” the gate is zero *unresolved* threads, not zero observations acted on. Re-pushing for another CodeRabbit pass to chase immaterial items is exactly the churn the materiality bar exists to stop. ## Concurrency With Other Reviewers (Mode 2) diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index df2d42f..c231e05 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -268,8 +268,11 @@ than waiting it out. missing from a list the artifact declares non-exhaustive โ€” go into one closing note and MUST NOT drive another iteration. - **Fix real issues** in code and tests; make atomic commits for the fixes. -- **Nitpicks** may be applied or consciously skipped โ€” don't churn on style the - project doesn't care about. There are no PR threads to resolve here (this is +- **Nitpicks are immaterial by definition, so do not apply them.** They go in + the closing note. Applying one produces a commit and Step 3 reruns against it, + manufacturing the next pass to change something that changes nothing. If a + nitpick turns out to clear the bar, it was never a nitpick: fix it as the + blocking finding it is. There are no PR threads to resolve here (this is local); resolution = the code is fixed (or the finding is a deliberate non-issue). - **Verify before fixing.** A reviewer's premise can be wrong. When a finding @@ -331,6 +334,11 @@ note, not findings. Where this artifact declares a list illustrative and a rule authoritative, assess the RULE. A further missing list entry is not a finding. +Where the artifact admits a limit and gates it โ€” "verified by X at +implementation time", "open question gated on Y" โ€” that is a disposition, not a +gap. Check the gate is real and sequenced before the thing that depends on it, +and do not report the limit itself as a missing step. + Where it records a decision with its rationale โ€” including "unknown, gated on X" โ€” and your disagreement is about preference, that is settled: say so once as an escalation, and do not re-argue it. This does NOT cover a decision that is diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index 5d210c1..8c84b24 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -209,7 +209,15 @@ mutation { ### 4. Handle Each Category #### Nitpicks (`[nitpick]` prefix) -- Resolve immediately without changes + +The prefix is Copilot's label, not a verdict. **Run the filters first** (scope โ†’ +contract โ†’ materiality, `fx-dev/skills/dev/references/scope-contract.md`): a +project-rule, security, privacy or correctness defect carrying this prefix is +blocking and is fixed. Only once it clears none of the filters is it a nitpick. + +Then: +- Resolve without changes โ€” no edit, because an edit reopens the loop for + something that changes nothing - Optional brief acknowledgment reply #### Outdated/Incorrect Copilot Comments @@ -276,11 +284,17 @@ non-exhaustive. **When feedback is valid but out of scope for the current PR:** -1. **Load the `fx-dev:project-management` skill** to track the follow-up work -2. **Add task to PROJECT.md** under the appropriate feature/section: - - Read current PROJECT.md structure - - Add a concise task describing the improvement - - Commit the PROJECT.md update +**Do not edit or commit anything in this PR.** The canonical `deferred` +disposition (`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver +dispositions) is reply-and-resolve with no edit; committing a tracker update +widens the change and reopens the review loop for work this PR deliberately is +not doing. + +1. **Return the follow-up to the coordinator** so it is recorded outside this + PR โ€” in the finding ledger, the PR description, or a tracker updated in a + separate change. When this skill runs standalone with no coordinator and the + repo tracks follow-ups in `PROJECT.md`, propose the entry to the user rather + than committing it here. 3. **Reply to the thread** explaining the deferral: - "Valid suggestion. Tracked as follow-up task in PROJECT.md for a future PR." 4. **Resolve the thread** diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 4850724..9504d70 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -331,7 +331,7 @@ Proceed to Step 5 when all of the following are true: 3. Every available review channel completed its initial pass or has a documented permitted degradation. 4. One verification pass over the latest affected delta leaves no blocking finding unresolved โ€” including any carried from an earlier pass. -`follow-up/out-of-scope` entries and non-contract suggestions do not block PR creation. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. Reaching the bound is a failure to converge and does not authorize Step 5. STOP, report the per-pass trend and everything still open, and let the user decide whether to create the PR โ€” including when every remaining entry is `follow-up/out-of-scope` with tier `n/a`. A blocking entry at the bound is always an escalation; the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. +`follow-up/out-of-scope` entries with tier `n/a`, and immaterial observations, do not block PR creation. Nothing else is waivable here: a reviewer-originated Material or Substantive finding blocks even though no written requirement names it, exactly as item 1 above and `references/scope-contract.md` ยง Blocking say. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. Reaching the bound is a failure to converge and does not authorize Step 5. STOP, report the per-pass trend and everything still open, and let the user decide whether to create the PR โ€” including when every remaining entry is `follow-up/out-of-scope` with tier `n/a`. A blocking entry at the bound is always an escalation; the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. **Do not spend the headroom.** The bound is far above what a healthy channel needs; the exits that should actually end a loop โ€” converged, the same disagreement twice, a rising blocking count of one class โ€” all fire in single digits. A round that resolves only immaterial items is churn at any iteration number, and the convergence rule already forbids it. From 3b6d24688ee543b0aaa1a92fe775b830e2eabc0b Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 20:36:45 +0000 Subject: [PATCH 23/48] fix(fx-dev): point the last enumerations at the canonical definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 11 returned 4 findings. Trend: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5, 4. Same class as passes 7 and 10, one more level down. Every one is a copy that drifted narrower than the rule it paraphrases. - codex-review's and coderabbit-review's "watch the shape" bullets both said a pass with no blocking findings is converged. The canonical test is the ledger, not the latest pass, so a blocker carried from an earlier round could sit open behind a quiet one and the PR could open on it. Both now say "none left unresolved" and point at ยง Convergence. - The previous commit added the brief-and-dispositions requirement for CodeRabbit Mode 2 as a paragraph, but left the actual `Skill tool:` command above it invoking the resolver with a bare PR number. An agent executing the chronological step never reaches the paragraph. The command carries the payload now. - pr-reviewer's output template enumerated the three blocking kinds eleven lines after its Standards section was told not to. The copy had already drifted: its Material row omitted reviewer-originated privacy defects and contradictions that make the change unimplementable, so a reviewer filling in the template could leave out a genuine blocker. It now points at ยง Blocking and asks only that each finding be labelled with its kind. Verified afterwards that no "a pass with none" phrasing survives anywhere in the tree. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/coderabbit-review/SKILL.md | 5 +++-- plugins/fx-dev/skills/codex-review/SKILL.md | 7 +++++-- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 10 ++++------ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a1b7d64..0cb5295 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.24.6", + "version": "0.24.7", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 3cc4fb2..6f1c040 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": "3.11.6", + "version": "3.11.7", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 6cd7970..4ada341 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -115,7 +115,7 @@ Run `cr review --agent` again after fixes. **Repeat Steps 1 โ†’ 2 until no block **Converged does NOT mean zero output.** Waiting for silence spends full review cycles on wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. - **Cap at 15 iterations** (`fx-dev/skills/dev/references/scope-contract.md` ยง The iteration bound) โ€” a runaway backstop, not a target. Convergence is the goal; reaching the bound is a failure to converge, and you report it as an escalation rather than a pass. If CodeRabbit keeps flagging the same design decision across two passes, that is a human call, not more code edits โ€” escalate it **by name** and stop, without spending the remaining iterations. -- Watch the shape, and count only what blocks: blocking findings still arriving โ†’ keep going; a pass with none โ†’ converged, however many immaterial observations it produced; the same disagreement twice โ†’ escalate. +- Watch the shape, and count only what blocks: blocking findings still arriving โ†’ keep going; **none left unresolved** โ†’ converged, however many immaterial observations it produced; the same disagreement twice โ†’ escalate. Converged is the ledger test in `fx-dev/skills/dev/references/scope-contract.md` ยง Convergence โ€” a quiet latest pass does not discharge a blocker carried from an earlier one. - **Rate-limit exception:** stop immediately on throttling; do not consume iterations waiting for cooldowns. - When you stop, report the per-pass trend and whether the last round's fixes were themselves reviewed. @@ -157,7 +157,8 @@ Script exit codes: If the script reports unresolved CodeRabbit threads (count > 0), invoke the rabbit-feedback-resolver: ``` -Skill tool: skill="fx-dev:rabbit-feedback-resolver", args="" +Skill tool: skill="fx-dev:rabbit-feedback-resolver", + args=" โ€” โ€” dispositions: blocking, immaterial, deferred ()" ``` That skill handles per-thread categorisation, pushes any code fixes, replies, and resolves each thread. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index c231e05..a4036e3 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -357,8 +357,11 @@ If the artifact is internally consistent and matches the tree, say so plainly. name** and stop rather than spending the remaining iterations. Each Codex pass on a real branch takes many minutes, so fix causes, not instances. - Watch the shape, and count only what blocks. Blocking findings still arriving - โ†’ keep going. A pass with none โ†’ converged, however many immaterial - observations it produced. The same disagreement twice โ†’ escalate. + โ†’ keep going. **None left unresolved** โ†’ converged, however many immaterial + observations it produced. The same disagreement twice โ†’ escalate. Converged is + the ledger test in `fx-dev/skills/dev/references/scope-contract.md` + ยง Convergence โ€” a quiet latest pass does not discharge a blocker carried from + an earlier one. - **A rising count means the last fix caused it.** If a pass returns more blocking findings than the one before and they are all the same class, stop fixing instances and fix the cause. When the cause is a design choice with two diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index 94ac91d..515c809 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -139,12 +139,10 @@ For projects with vendor submodules (e.g., `vendor/` directory): **Ready**: YES/NO ### Blocking -- [Contract blockers: a project, security, or privacy rule violation โ€” blocking - by virtue of being a rule, never ranked by the bar] -- [Material findings: wrong behaviour, data loss, security, a build or test that - will fail, a stated fact that is false AND that a reader would act on] -- [Substantive findings: a genuine ambiguity a reader could act on two ways, a - missing step that would be discovered late] +- [Every blocking finding, as defined in + `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking โ€” do not restate or + narrow that definition here. Label each one with its kind: contract blocker, + Material, or Substantive.] ### Closing note (non-blocking) - [One unnumbered paragraph for everything that clears neither tier โ€” wording, From d69ce46c3efc9cfb2b00bec27cbd9b46c4432fd6 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 20:54:48 +0000 Subject: [PATCH 24/48] fix(fx-dev): finish the define-once sweep and the deferred no-edit rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 12 returned 3 findings. Trend: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5, 4, 3. Four agent-readable copies of the blocking definition survived, in pr-reviewer's reporting rule, resolve-pr-feedback's push rule, and two places in copilot-feedback-resolver. Each could be read as exhaustive while the following clause routed everything else to the closing note, so a mandatory requirement outside the examples, or a late-discovered missing step, could be downgraded. All four now reference ยง Blocking. Only the external Codex mirror inlines it. coderabbit-review's Mode 1 success criteria declared success from the latest `cr` output, with the carried-finding rule hidden in a second bullet whose "before any limit" wording read as applying only to the rate-limit path. It now states the ledger test directly, and says explicitly that the rate-limit degradation waives the unrun remainder of the review rather than a finding already delivered. The deferred disposition was the worst of the three: the no-edit handler added two commits ago contradicted five other places in the same file that still required a `PROJECT.md` commit โ€” the category table, the CRITICAL rule, the completion criterion, the reply template and the summary-table example. An agent had to either widen the PR with a tracker commit or fail its own stated completion criteria. All five now agree: reply citing the exclusion, resolve, and return the follow-up to the coordinator; when running standalone, propose the entry rather than committing it. The requirement that a deferral be recorded somewhere durable is kept โ€” it just cannot be a commit on this branch. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 5 +++-- .../skills/copilot-feedback-resolver/SKILL.md | 17 ++++++++--------- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 10 ++++------ .../fx-dev/skills/resolve-pr-feedback/SKILL.md | 2 +- 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0cb5295..2399313 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.24.7", + "version": "0.24.8", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 6f1c040..a5fa19b 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": "3.11.7", + "version": "3.11.8", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 4ada341..600d00b 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -218,8 +218,9 @@ Never call the Agent tool from inside a sub-agent context. ## Success Criteria **Mode 1 (local, primary):** -- โœ… `cr review --agent` reports **no blocking findings** after fixes, **or** the service rate-limited and the pass is recorded as `skipped (rate-limited)`. Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar -- โœ… All blocking findings received before any limit are resolved and committed +- โœ… **No blocking finding is left unresolved** โ€” the ledger test in `fx-dev/skills/dev/references/scope-contract.md` ยง Convergence, covering every blocking finding received across all passes, not only what the latest `cr` output repeated. A blocker an earlier pass raised and this one did not still blocks +- โœ… That holds on a normal run **and** on the rate-limited path, where the degradation waives only the *unrun* remainder of the review, never a finding already delivered. Then the pass is recorded as `skipped (rate-limited)` +- โœ… Remaining immaterial observations do not block โ€” they are carried as one closing note, per the materiality bar - โœ… No cooldown waits or retries remain when the rate-limit exception applies **Mode 2 (PR-level, fallback / optional merge gate):** diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index 8c84b24..34ebd94 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -186,9 +186,9 @@ carrying it is still blocking. Never auto-resolve on the prefix alone. | **Nitpick** | Contains `[nitpick]` prefix **and clears none of the filters below** | Reply and resolve without editing | | **Outdated** | Refers to code that no longer exists | Reply with explanation, resolve | | **Incorrect** | Misunderstands project conventions | Reply with explanation, resolve, update `REVIEW.md` | -| **Valid โ€” blocking** | Current concern that clears the bar: a project-rule, security or privacy violation, or a Material or Substantive finding (`fx-dev/skills/dev/references/scope-contract.md`) | Delegate to coder sub-agent to fix | +| **Valid โ€” blocking** | Clears the bar โ€” see `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking; do not narrow it here | Delegate to coder sub-agent to fix | | **Valid โ€” immaterial** | Correct, but would change nothing if it shipped uncorrected | Reply with that reasoning, resolve. **Do not edit** โ€” a fix push reopens the review loop for an item that changes nothing | -| **Deferred** | Valid but out of scope for this PR | Track in PROJECT.md, reply, resolve | +| **Deferred** | Valid but out of scope for this PR | Reply citing the exclusion, resolve. **No edit and no commit** โ€” return the follow-up to the coordinator (`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver dispositions) | ### 3. Resolve Threads @@ -257,9 +257,8 @@ mutation { #### Valid โ€” blocking -Only for concerns that clear the bar (`fx-dev/skills/dev/references/scope-contract.md` -ยง Blocking): a project-rule, security or privacy violation, or a Material or -Substantive finding. +Only for concerns that clear the bar โ€” `fx-dev/skills/dev/references/scope-contract.md` +ยง Blocking is the definition, and this section does not restate it. 1. Delegate to coder sub-agent with: - PR number and title @@ -296,10 +295,10 @@ not doing. repo tracks follow-ups in `PROJECT.md`, propose the entry to the user rather than committing it here. 3. **Reply to the thread** explaining the deferral: - - "Valid suggestion. Tracked as follow-up task in PROJECT.md for a future PR." + - "Valid suggestion, but out of scope for this PR: . Returned to the coordinator as a follow-up rather than tracked here, so this PR is not widened." 4. **Resolve the thread** -**CRITICAL:** Never defer feedback without tracking it. "Acknowledged for follow-up" without a PROJECT.md entry is INCOMPLETE WORK. +**CRITICAL:** Never defer feedback without recording it somewhere durable โ€” but that record must not be a commit on this PR. Return it to the coordinator for the finding ledger or the PR description; when this skill runs standalone and the repo tracks follow-ups in `PROJECT.md`, propose the entry to the user instead of committing it. A bare "acknowledged for follow-up" with no record anywhere is INCOMPLETE WORK; a `PROJECT.md` commit in this PR is a widened change. ### 5. Verify Completion @@ -326,7 +325,7 @@ This suggestion conflicts with our [convention name] convention. [Brief explanat 1. โœ… All code changes pushed to the PR branch 2. โœ… **EVERY addressed thread resolved via GraphQL mutation** (not just code fixed!) 3. โœ… **For INCORRECT feedback: `REVIEW.md` updated** to prevent recurrence -4. โœ… **For DEFERRED feedback: Task added to `docs/PROJECT.md`** via project-management skill +4. โœ… **For DEFERRED feedback: the follow-up is recorded outside this PR** โ€” returned to the coordinator, or proposed to the user when running standalone. **No `PROJECT.md` commit on this branch** 5. โœ… Re-query confirms `isResolved: true` for all processed threads 6. โœ… Output summary table (see format below) @@ -340,7 +339,7 @@ This suggestion conflicts with our [convention name] convention. [Brief explanat | PRRT_xxx | src/foo.ts:42 | Nitpick | Auto-resolved | โœ… Resolved | | PRRT_yyy | src/bar.ts:15 | Valid | Fixed null check | โœ… Resolved | | PRRT_zzz | lib/util.js:8 | Outdated | Code refactored | โœ… Resolved | -| PRRT_aaa | src/ui.tsx:20 | Deferred | Tracked in PROJECT.md | โœ… Resolved | +| PRRT_aaa | src/ui.tsx:20 | Deferred | Returned to coordinator, no edit | โœ… Resolved | ``` **Column definitions:** diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index 515c809..3c04f38 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -46,12 +46,10 @@ Then review within it: Scope decides whether a finding is the author's problem. **Materiality decides whether it is worth their time** โ€” see the materiality bar in the same reference. -- **Report every blocking finding individually.** Wrong behaviour, - data loss, security, a build or test that will fail, a contradiction that makes - the change unimplementable, a stated fact that is false and that a reader would - act on, or a genuine ambiguity - a reader could act on two ways. -- **Collect everything else into one closing note**, unnumbered and explicitly +- **Report every blocking finding individually**, as defined in `fx-dev/skills/dev/references/scope-contract.md` + ยง Blocking. Do not restate that definition here; the examples there are + illustrative and the rule is authoritative. +- **Collect everything immaterial into one closing note**, unnumbered and explicitly non-blocking. Wording, formatting, naming preference, a count nothing keys on. - When unsure of the tier, ask: *if this shipped uncorrected, what breaks?* If the honest answer is "nothing, it is just not as good as it could be", it diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index d84da1d..309db52 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -25,7 +25,7 @@ Scope decides whether a finding is *ours*. Materiality decides whether it is *wo An in-scope item that would change nothing if it shipped uncorrected โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact itself declares non-exhaustive โ€” is an **observation, not a finding**, and is **resolved by replying with that reasoning** rather than by editing. Every thread still ends resolved; the gate is zero *unresolved* threads, not zero observations acted on. -This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for blocking findings โ€” every contract blocker, Material and Substantive finding; reply-and-resolve only the immaterial rest in the same cycle. A contract blocker is never discharged by a reply explaining it: the artifact has to change. +This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for blocking findings (`fx-dev/skills/dev/references/scope-contract.md` ยง Blocking); reply-and-resolve only the immaterial rest in the same cycle. A contract blocker is never discharged by a reply explaining it: the artifact has to change. **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the code, and when it does not hold, reply with the evidence and resolve rather than changing working code to satisfy a misreading. From 424d6c828799a16e4c04303699efb784c9ec5548 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 21:12:37 +0000 Subject: [PATCH 25/48] fix(fx-dev): stop unranked blockers falling into the closing note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 13 returned 3 findings. Trend: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5, 4, 3, 3. Two are the same subtle error, introduced by the bar itself: treating "clears the bar" as the test for blocking. A contract blocker never passes through the bar โ€” filter 2 stops before it, which is exactly why it carries tier `n/a` โ€” so any rule phrased around clearing the bar silently excludes the category the project cares most about. pr-reviewer's template routed "everything that clears neither tier" into the non-blocking closing note. A project-rule violation clears neither tier, so the template demoted it, while the sentence after it said the opposite. The closing note is now explicitly for immaterial observations only, deferred findings get their own section rather than being folded in, and the template says why "clears neither tier" is the wrong test. copilot-feedback-resolver's valid-blocking row and handler both said "clears the bar", so a tier-`n/a` contract blocker found on the direct-invocation path could be classified somewhere other than the fix path. Both now ask whether the finding **is blocking**, and say a contract blocker qualifies without being ranked. Third, dev's PR-ready gate and its Mode B stopping rule each restated the convergence test inline rather than referencing ยง Convergence, which is what the define-once rule requires precisely so a carried or suppressed blocker cannot be lost when one copy is edited. Both now reference it and keep the latest-delta pass as an additional gate on top of canonical convergence. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/copilot-feedback-resolver/SKILL.md | 8 +++++--- plugins/fx-dev/skills/dev/SKILL.md | 4 ++-- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 12 +++++++++--- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 2399313..6b7931a 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.24.8", + "version": "0.24.9", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index a5fa19b..4a3f315 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": "3.11.8", + "version": "3.11.9", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index 34ebd94..4f289dd 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -186,7 +186,7 @@ carrying it is still blocking. Never auto-resolve on the prefix alone. | **Nitpick** | Contains `[nitpick]` prefix **and clears none of the filters below** | Reply and resolve without editing | | **Outdated** | Refers to code that no longer exists | Reply with explanation, resolve | | **Incorrect** | Misunderstands project conventions | Reply with explanation, resolve, update `REVIEW.md` | -| **Valid โ€” blocking** | Clears the bar โ€” see `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking; do not narrow it here | Delegate to coder sub-agent to fix | +| **Valid โ€” blocking** | **Is blocking** per `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking โ€” which includes a contract blocker, and those never pass through the bar at all. Do not narrow it here | Delegate to coder sub-agent to fix | | **Valid โ€” immaterial** | Correct, but would change nothing if it shipped uncorrected | Reply with that reasoning, resolve. **Do not edit** โ€” a fix push reopens the review loop for an item that changes nothing | | **Deferred** | Valid but out of scope for this PR | Reply citing the exclusion, resolve. **No edit and no commit** โ€” return the follow-up to the coordinator (`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver dispositions) | @@ -257,8 +257,10 @@ mutation { #### Valid โ€” blocking -Only for concerns that clear the bar โ€” `fx-dev/skills/dev/references/scope-contract.md` -ยง Blocking is the definition, and this section does not restate it. +For any concern that **is blocking** under `fx-dev/skills/dev/references/scope-contract.md` +ยง Blocking, which this section does not restate. Note that a contract blocker โ€” +a project rule, or a security or privacy invariant โ€” is blocking without ever +being ranked by the bar, so "did it clear the bar" is the wrong question for one. 1. Delegate to coder sub-agent with: - PR number and title diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 9504d70..7256609 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -329,7 +329,7 @@ Proceed to Step 5 when all of the following are true: **`follow-up/out-of-scope` with a Material or Substantive tier is not a legal combination.** The two fields are assigned by different filters and cannot disagree: an out-of-scope finding is deferred at filter 1 and never reaches the bar, so its tier is `n/a`; a finding that *does* reach the bar is in scope by construction, so if it ranks Material or Substantive it is a defect in work this change actually did, and belongs under `required-by-contract` or `regression-caused-by-change`. If you are about to record that pair, one of the two filters was misapplied โ€” re-run them rather than writing an entry the gate can neither clear nor waive. Materiality never promotes an out-of-scope finding back into scope (`references/scope-contract.md` ยง Three filters); this rule is that principle applied to the ledger. 2. Contract-required tests and tests affected by the latest delta pass. 3. Every available review channel completed its initial pass or has a documented permitted degradation. -4. One verification pass over the latest affected delta leaves no blocking finding unresolved โ€” including any carried from an earlier pass. +4. The review has **converged** as `references/scope-contract.md` ยง Convergence defines it โ€” no blocking finding left unresolved, ledger-wide, not merely none new in the latest pass. As an additional gate, that state is confirmed by one verification pass over the latest affected delta. `follow-up/out-of-scope` entries with tier `n/a`, and immaterial observations, do not block PR creation. Nothing else is waivable here: a reviewer-originated Material or Substantive finding blocks even though no written requirement names it, exactly as item 1 above and `references/scope-contract.md` ยง Blocking say. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. Reaching the bound is a failure to converge and does not authorize Step 5. STOP, report the per-pass trend and everything still open, and let the user decide whether to create the PR โ€” including when every remaining entry is `follow-up/out-of-scope` with tier `n/a`. A blocking entry at the bound is always an escalation; the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. @@ -647,7 +647,7 @@ Concrete recipe: 2. In the foreground, wait for Copilot using the bundled `copilot-review` waiter, then read its unresolved threads without invoking a resolver. Classify and deduplicate them in the coordinator-owned ledger. 3. Wait for the background CodeRabbit waiter to finish, then read its unresolved threads and classify them before invoking a resolver. Invoke each reviewer resolver only after classification, passing the blocking findings and instructing it to settle deferred findings without code or task-tracker changes. 4. After a resolver pushes, record the new SHA and inspect only feedback added or changed since the previous reviewed SHA. Classify and deduplicate it in the shared ledger. Rerun only the reviewer whose state or evidence the delta invalidated; do not restart every reviewer merely because `HEAD` changed. -5. Stop after one latest-delta pass leaves no blocking finding unresolved โ€” including any carried from an earlier pass โ€” and every required reviewer thread is settled. Blocking is defined in `references/scope-contract.md` ยง Blocking. +5. Stop when the channel has **converged** per `references/scope-contract.md` ยง Convergence โ€” no blocking finding left unresolved, ledger-wide โ€” confirmed by one latest-delta pass, and every required reviewer thread is settled. If `Bash` `run_in_background` isn't available in your context, fall back to fully-serial: Copilot first, then CodeRabbit. Slower but correct. diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index 3c04f38..2e0d04a 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -142,11 +142,17 @@ For projects with vendor submodules (e.g., `vendor/` directory): narrow that definition here. Label each one with its kind: contract blocker, Material, or Substantive.] +### Deferred (out of scope, not blocking) +- [Each finding the Scope Brief excludes, with the exclusion that covers it. + Reported, never silently dropped, and never fixed here.] + ### Closing note (non-blocking) -- [One unnumbered paragraph for everything that clears neither tier โ€” wording, +- [One unnumbered paragraph for **immaterial** observations only โ€” wording, formatting, naming preference, counts nothing keys on, optional improvements. - There is no middle tier: if it is in scope and does not block, it belongs - here, collapsed into prose and never itemized as findings.] + Nothing blocking belongs here: a contract blocker carries no materiality tier + (`fx-dev/skills/dev/references/scope-contract.md` ยง Three filters) and is still + blocking, so "clears neither tier" is not the test. In scope and not + immaterial means it goes under Blocking.] ### Next - [Clear actions] From f75aa3213149ee2cfa578704e0dff98bfa1e9ab2 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 21:30:45 +0000 Subject: [PATCH 26/48] fix(fx-dev): honour dispositions in the rabbit resolver, fix the off-by-one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 14 returned 3 findings. Trend: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5, 4, 3, 3, 3 blocking. rabbit-feedback-resolver was the mirror image of the copilot resolver two commits ago, and had not been swept: it auto-resolved on CodeRabbit's own `_๐Ÿงน Nitpick_` / `_๐Ÿ”ต Trivial_` labels, applied committable suggestions on sight, and committed a PROJECT.md entry for deferred work โ€” none of it honouring the dispositions coderabbit-review now passes it. A project-rule or security violation CodeRabbit happened to label a nitpick was therefore closed without reaching the filters, and a deferred thread widened the PR. It now takes coordinator dispositions as authoritative, runs the filters itself when it has none, treats the labels as inputs, verifies committable suggestions against the tree before applying, and returns follow-ups instead of committing them. This file is not otherwise part of this change; the bar made it inconsistent, so it is a regression this change caused. The Codex mirror was attached only to re-run prompts, so the initial pass โ€” the one where a reviewer with no bar produces the largest crop of low-value findings, and therefore where it matters most โ€” went out without it. Every prompt carries it now; re-runs add the convergence header on top. Last, an off-by-one in the bound. dev allowed 15 remediation rounds *after* an initial pass its own item 3 requires, which is 16 reviewer invocations against a canonical cap of 15. Both the canonical rule and dev now state that the initial pass is iteration 1, and that counting from after it buys a sixteenth. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- plugins/fx-dev/skills/codex-review/SKILL.md | 6 ++- plugins/fx-dev/skills/dev/SKILL.md | 2 +- .../skills/dev/references/scope-contract.md | 2 +- .../skills/rabbit-feedback-resolver/SKILL.md | 40 ++++++++++++++----- 6 files changed, 38 insertions(+), 16 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6b7931a..4456f98 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.24.9", + "version": "0.24.10", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 4a3f315..17c7c1f 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": "3.11.9", + "version": "3.11.10", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index a4036e3..2d5f2f5 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -300,8 +300,10 @@ wording. Stop when what remains would change nothing if it shipped uncorrected, and list those items once, non-blocking. **Tell Codex the bar and the settled ground in the prompt**, so it spends the -pass where it pays. Every re-run prompt MUST carry, in addition to the Scope -Brief: +pass where it pays. **Every prompt carries this block โ€” the first pass as well as +every re-run.** Pass 1 is where a reviewer with no bar produces the largest crop +of low-value findings, so omitting it there costs the most; on re-runs the +CONVERGENCE PASS line and the do-not-re-report list are added on top. > **This block is a MIRROR** of `fx-dev/skills/dev/references/scope-contract.md` > ยง Blocking and ยง Three things that are not findings โ€” the one case the diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 7256609..61c4d8b 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -331,7 +331,7 @@ Proceed to Step 5 when all of the following are true: 3. Every available review channel completed its initial pass or has a documented permitted degradation. 4. The review has **converged** as `references/scope-contract.md` ยง Convergence defines it โ€” no blocking finding left unresolved, ledger-wide, not merely none new in the latest pass. As an additional gate, that state is confirmed by one verification pass over the latest affected delta. -`follow-up/out-of-scope` entries with tier `n/a`, and immaterial observations, do not block PR creation. Nothing else is waivable here: a reviewer-originated Material or Substantive finding blocks even though no written requirement names it, exactly as item 1 above and `references/scope-contract.md` ยง Blocking say. Each review channel caps at **15 remediation/delta-verification rounds** after its initial pass โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. Reaching the bound is a failure to converge and does not authorize Step 5. STOP, report the per-pass trend and everything still open, and let the user decide whether to create the PR โ€” including when every remaining entry is `follow-up/out-of-scope` with tier `n/a`. A blocking entry at the bound is always an escalation; the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. +`follow-up/out-of-scope` entries with tier `n/a`, and immaterial observations, do not block PR creation. Nothing else is waivable here: a reviewer-originated Material or Substantive finding blocks even though no written requirement names it, exactly as item 1 above and `references/scope-contract.md` ยง Blocking say. Each review channel caps at **15 reviewer invocations in total, counting its initial pass** โ€” the single bound defined in `references/scope-contract.md` ยง The iteration bound, which no skill overrides. The initial pass is iteration 1, leaving 14 remediation/delta-verification rounds; counting the bound from *after* the initial pass would make it 16. **Convergence is the goal, and the bound is a runaway backstop, not a target.** Reaching it means the loop failed to converge; report it that way. Reaching the bound is a failure to converge and does not authorize Step 5. STOP, report the per-pass trend and everything still open, and let the user decide whether to create the PR โ€” including when every remaining entry is `follow-up/out-of-scope` with tier `n/a`. A blocking entry at the bound is always an escalation; the bound never waives one. A contract amendment may change product scope, but it cannot waive mandatory correctness, security, privacy, testing, or merge rules. **Do not spend the headroom.** The bound is far above what a healthy channel needs; the exits that should actually end a loop โ€” converged, the same disagreement twice, a rising blocking count of one class โ€” all fire in single digits. A round that resolves only immaterial items is churn at any iteration number, and the convergence rule already forbids it. diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 46a004d..4170004 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -156,7 +156,7 @@ Report the trend when you stop, so the operator can see the shape rather than ta **Convergence is the goal. The bound is a runaway backstop, not a target, and reaching it is a failure to converge โ€” never a stopping condition you are entitled to treat as success.** -**Every review-convergence loop caps at 15 iterations.** That is the single number; skills MUST NOT set their own. It is deliberately far above what a healthy loop needs โ€” a review that is working converges in single digits โ€” so that hitting it means something is wrong rather than that the work was merely large. +**Every review-convergence loop caps at 15 iterations, and the initial pass is iteration 1.** That is the single number; skills MUST NOT set their own, and none may count the bound from *after* the first pass โ€” that quietly buys a sixteenth. It is deliberately far above what a healthy loop needs โ€” a review that is working converges in single digits โ€” so that hitting it means something is wrong rather than that the work was merely large. A loop should almost always end on one of the three signals above, all of which fire long before 15: diff --git a/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md index cec158c..a6957b4 100644 --- a/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md @@ -161,18 +161,37 @@ query { For each unresolved CodeRabbit comment: +**If the coordinator supplied per-thread dispositions, they win.** A disposition +from `fx-dev:coderabbit-review` or `fx-dev:resolve-pr-feedback` is set with the +Scope Brief and the finding ledger in hand; this table classifies from comment +text alone. Apply the disposition (`fx-dev/skills/dev/references/scope-contract.md` +ยง Resolver dispositions) and use the table only for threads it did not cover. A +thread marked `blocking` is fixed even when CodeRabbit labelled it +`_๐Ÿงน Nitpick_`; a thread marked `immaterial` or `deferred` is replied to and +resolved **without editing anything**. + +**With no disposition, run the filters yourself** โ€” scope, then contract, then +materiality (`fx-dev/skills/dev/references/scope-contract.md` ยง Three filters). +CodeRabbit's own labels are an *input* to that judgment, never a verdict. + | Category | Indicator | Action | |----------|-----------|--------| -| **Nitpick/Trivial** | Contains `_๐Ÿงน Nitpick_` or `_๐Ÿ”ต Trivial_` | Auto-resolve immediately | +| **Nitpick/Trivial** | Carries `_๐Ÿงน Nitpick_` or `_๐Ÿ”ต Trivial_` **and clears none of the filters** | Reply and resolve, no edit | | **Actionable with AI Prompt** | Has `๐Ÿค– Prompt for AI Agents` section | Extract prompt, delegate to coder | -| **Actionable with Committable** | Has `๐Ÿ“ Committable suggestion` | Apply suggestion directly | +| **Actionable with Committable** | Has `๐Ÿ“ Committable suggestion` **and is blocking** per `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking | Verify the suggestion against the code, then apply. Never apply on sight โ€” a committable suggestion is still a claim about the tree | | **General Feedback** | No special sections | Analyze and delegate to coder | -| **Deferred** | Valid but out of scope for this PR | Track in PROJECT.md, reply, resolve | +| **Deferred** | Valid but out of scope for this PR | Reply citing the exclusion, resolve. **No edit and no commit** โ€” return the follow-up to the coordinator (`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver dispositions) | ### 3. Process Each Category #### Nitpicks/Trivial -- Resolve immediately without changes + +The label is CodeRabbit's, not a verdict. **Run the filters first**: a +project-rule, security, privacy or correctness defect carrying it is blocking +and is fixed. Only once it clears none of them is it a nitpick. + +- Resolve without changes โ€” an edit reopens the loop for something that changes + nothing - These are suggestions, not requirements #### Actionable with AI Prompt (PREFERRED) @@ -212,15 +231,16 @@ values... **When feedback is valid but out of scope for the current PR:** 1. **Load the `fx-dev:project-management` skill** to track the follow-up work -2. **Add task to PROJECT.md** under the appropriate feature/section: - - Read current PROJECT.md structure - - Add a concise task describing the improvement - - Commit the PROJECT.md update +2. **Return the follow-up to the coordinator** โ€” the finding ledger or the PR + description. **Do not edit or commit anything in this PR**: the canonical + `deferred` disposition is reply-and-resolve with no edit, and a tracker commit + widens the change and reopens the review loop. Running standalone with no + coordinator, propose the entry to the user rather than committing it. 3. **Reply to the thread** explaining the deferral: - - "Valid suggestion. Tracked as follow-up task in PROJECT.md for a future PR." + - "Valid suggestion, but out of scope for this PR: . Returned as a follow-up rather than tracked here, so this PR is not widened." 4. **Resolve the thread** -**CRITICAL:** Never defer feedback without tracking it. "Acknowledged for follow-up" without a PROJECT.md entry is INCOMPLETE WORK. +**CRITICAL:** Never defer feedback without recording it somewhere durable โ€” but that record must not be a commit on this PR. A bare "acknowledged for follow-up" with no record anywhere is INCOMPLETE WORK; a `PROJECT.md` commit on this branch is a widened change. ### 4. Resolve Threads From 3a191f951b201549f45a19b4d0773d365dd9f42c Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 21:41:37 +0000 Subject: [PATCH 27/48] fix(fx-dev): finish the rabbit resolver sweep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex pass 15 โ€” the last iteration of the bound โ€” returned 3 findings, all in rabbit-feedback-resolver, all the same shape: the category table was brought to the bar last commit while the summary and the detailed handlers it points at were not. - The "Key elements to extract" summary still declared `_๐Ÿงน Nitpick_` and `_๐Ÿ”ต Trivial_` "auto-resolvable", contradicting the table row that requires filtering. All three key elements are now stated as inputs to triage rather than verdicts. - The AI-prompt and General Feedback paths delegated to a coder unconditionally, with no immaterial branch. On a direct invocation a formatted comment that ranks Immaterial would therefore have produced an edit and a push instead of reply-and-resolve โ€” manufacturing the next round's input. Both are now blocking-only. - The committable-suggestion handler still said to apply the change directly, bypassing the verify-before-apply requirement the table added. It now verifies first, for the documented reason: a suggestion applied as written has been observed to introduce the very bug it claimed to report. Findings per pass across the whole run: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5, 4, 3, 3, 3, 3 blocking. The loop reached the 15-iteration bound without converging, and these fixes have not themselves been reviewed. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../skills/rabbit-feedback-resolver/SKILL.md | 49 ++++++++++++++----- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4456f98..5be2b53 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.24.10", + "version": "0.24.11", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 17c7c1f..b570aa9 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": "3.11.10", + "version": "3.11.11", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md index a6957b4..bb75f44 100644 --- a/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md @@ -54,10 +54,17 @@ _๐Ÿงน Nitpick_ | _๐Ÿ”ต Trivial_ <- Severity indicator (optional)
``` -**Key elements to extract:** -- **Severity**: `_๐Ÿงน Nitpick_` or `_๐Ÿ”ต Trivial_` = auto-resolvable -- **Prompt for AI Agents**: Explicit instructions - USE THESE DIRECTLY -- **Committable suggestion**: Ready-to-apply code changes +**Key elements to extract.** All three are *inputs* to triage, never verdicts โ€” +the disposition comes from the coordinator, or from the filters you run yourself +(`fx-dev/skills/dev/references/scope-contract.md` ยง Three filters): +- **Severity**: `_๐Ÿงน Nitpick_` or `_๐Ÿ”ต Trivial_` is CodeRabbit's own label. It + suggests immaterial; it does not establish it. A project-rule, security, + privacy or correctness defect carrying it is still blocking. +- **Prompt for AI Agents**: explicit instructions. Use them for a **blocking** + finding, after checking the premise holds. +- **Committable suggestion**: ready-to-apply code. Verify it against the tree + before applying โ€” a suggestion is a claim, and an applied-on-sight one has + introduced the bug it claimed to report. ## Prerequisites @@ -177,9 +184,9 @@ CodeRabbit's own labels are an *input* to that judgment, never a verdict. | Category | Indicator | Action | |----------|-----------|--------| | **Nitpick/Trivial** | Carries `_๐Ÿงน Nitpick_` or `_๐Ÿ”ต Trivial_` **and clears none of the filters** | Reply and resolve, no edit | -| **Actionable with AI Prompt** | Has `๐Ÿค– Prompt for AI Agents` section | Extract prompt, delegate to coder | +| **Actionable with AI Prompt** | Has `๐Ÿค– Prompt for AI Agents` section **and is blocking** per `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking | Verify the premise, then extract the prompt and delegate to coder | | **Actionable with Committable** | Has `๐Ÿ“ Committable suggestion` **and is blocking** per `fx-dev/skills/dev/references/scope-contract.md` ยง Blocking | Verify the suggestion against the code, then apply. Never apply on sight โ€” a committable suggestion is still a claim about the tree | -| **General Feedback** | No special sections | Analyze and delegate to coder | +| **General Feedback** | No special sections | Triage first; delegate to coder only if **blocking**, otherwise reply and resolve with no edit | | **Deferred** | Valid but out of scope for this PR | Reply citing the exclusion, resolve. **No edit and no commit** โ€” return the follow-up to the coordinator (`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver dispositions) | ### 3. Process Each Category @@ -196,11 +203,19 @@ and is fixed. Only once it clears none of them is it a nitpick. #### Actionable with AI Prompt (PREFERRED) -**When a comment contains `๐Ÿค– Prompt for AI Agents`, extract and use it directly:** +**Only for a thread whose disposition is `blocking`.** The presence of an AI +prompt says nothing about materiality: a formatted comment can still rank +Immaterial, and then it is replied to and resolved with no edit, exactly like any +other immaterial thread. Check the disposition first. + +For a blocking one: 1. Parse the comment body to extract content between `๐Ÿค– Prompt for AI Agents` and the closing `
` -2. The extracted text contains explicit instructions - pass these to the coder sub-agent verbatim -3. After fix is implemented, resolve the thread +2. **Verify the premise before acting.** The prompt asserts something about the + tree; when that does not hold, reply with the evidence and resolve rather than + changing working code to satisfy a misreading +3. Pass the extracted instructions to the coder sub-agent verbatim +4. After the fix is implemented, resolve the thread Example extraction: ``` @@ -211,10 +226,18 @@ values... #### Actionable with Committable Suggestion -1. Extract the code block from `๐Ÿ“ Committable suggestion` section -2. Apply the suggested changes directly using Edit tool -3. Commit with message referencing the CodeRabbit suggestion -4. Resolve the thread +**Only for a thread whose disposition is `blocking`** โ€” a committable suggestion +attached to an immaterial observation is still immaterial, and applying it +produces the push that reopens the loop. + +1. Extract the code block from the `๐Ÿ“ Committable suggestion` section +2. **Verify it against the code before applying.** Never apply on sight: a + suggestion is a claim about the tree, and one applied as written has been + observed to introduce the very bug it claimed to report. If the premise does + not hold, reply with the evidence and resolve instead +3. Apply the verified change using the Edit tool +4. Commit with a message referencing the CodeRabbit suggestion +5. Resolve the thread #### General Feedback From 61c243e31452e57cec5ce09ec859217802cf5864 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 21:49:32 +0000 Subject: [PATCH 28/48] feat(fx-dev): make a fix close the defect's class, not one instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 15-pass run on this branch produced 62 findings and never converged, and not one of those findings was wrong. Every premise held, every fix was correct, and the loop still would not end. The bar was working and the loop was still broken, which means the bar was not the whole problem. The cause is that a reviewer reports the instance it happened to read. Fixing exactly that instance leaves the siblings, and the next pass reports a sibling as a new finding. Every pass is then genuinely productive, every pass costs a full cycle, and the count drifts sideways for as long as the class has members. That is what the trend recorded: 4, 4, 8, 2, 3, 8, 6, 3, 3, 5, 4, 3, 3, 3, 3 โ€” six passes flat at three, each one finding real siblings of defects that had already been "fixed". scope-contract.md ยง Fix the class, not the instance makes the class the unit of work. A finding is an instance until proved unique; you name the defect pattern without reference to location, sweep it, and fix the sites together. The four sibling axes are drawn from where this run actually lost passes, not invented: - Lexical โ€” the same wording at other sites. Cost passes 3 and 5. - Altitude โ€” the same rule as summary, table row, step-by-step handler, checklist, success criterion, example, template or command block. A definition corrected while the procedure below it still described the old behaviour cost passes 10 through 13; a reader following the steps got the old rule. - Symmetry โ€” reviewer A and reviewer B, mode 1 and mode 2, the parallel resolver. copilot-feedback-resolver was fixed at pass 6 and its twin rabbit-feedback-resolver was not touched until pass 14. - Enumeration โ€” if one member of a list was wrong, the others usually are. Sweeps must be proved by a search whose empty result is the evidence, because two verifying searches in this run reported success wrongly: one was line-oriented against prose that wrapped, so the surviving phrase was invisible; the other filtered out the very file under review. Both are documented, and the rule requires stating the search alongside the claim so a wrong one is visible rather than trusted. Two bounds keep this from becoming its own failure. Past a handful of sites the duplication is the defect, so consolidate to one definition and references rather than syncing copies forever โ€” but that is a design decision to raise, not a large mechanical rewrite to perform unasked. And the class is bounded by the change's blast radius: the files it touches plus those it made inconsistent. A sibling outside that radius is a deferred follow-up, under the existing sprawl rule. Last, the loop rule the run kept violating: do not start another review pass with a class half-closed. A re-run against a partial fix spends a full cycle being told about siblings you already knew about, and its findings are indistinguishable from new ones. codex-review's re-run prompt now carries the classes closed since the last pass and the searches that proved them. The reviewer side gets the other half. pr-reviewer and the injected Codex prompt now require that a finding which is one instance of a visible pattern be reported as ONE finding naming the class and listing the sites โ€” not one finding per site, and not one site in silence. Codex did this unprompted on some passes, and those were the passes whose sweeps came out clean. Applied the rule to this commit: swept all eight skills that act on review findings. The first pass covered six and missed both feedback resolvers โ€” the symmetry axis, caught by the rule being written. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 1 + plugins/fx-dev/skills/codex-review/SKILL.md | 12 ++++++ .../skills/copilot-feedback-resolver/SKILL.md | 6 +++ plugins/fx-dev/skills/copilot-review/SKILL.md | 6 +++ plugins/fx-dev/skills/dev/SKILL.md | 2 + .../skills/dev/references/scope-contract.md | 40 +++++++++++++++++++ plugins/fx-dev/skills/pr-reviewer/SKILL.md | 6 +++ .../skills/rabbit-feedback-resolver/SKILL.md | 6 +++ .../skills/resolve-pr-feedback/SKILL.md | 2 + 11 files changed, 83 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 5be2b53..0370ef4 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.24.11", + "version": "0.25.0", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index b570aa9..524bd75 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": "3.11.11", + "version": "3.12.0", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 600d00b..6b2a1d0 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -103,6 +103,7 @@ Use `cr review --agent --base main` to scope to the branch's diff against `main` Treat findings like self-review feedback: - **Triage in the contract's order โ€” scope, then contract, then materiality** (`fx-dev/skills/dev/references/scope-contract.md`). An out-of-scope finding is deferred however material it looks, per the triage rules above; project rules and security/privacy invariants block regardless of the bar; only what remains is ranked. Blocking findings are fixed; immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go in one closing note and MUST NOT drive another iteration. CodeRabbit's own `๐ŸŸ  Major` / `๐ŸŸก Minor` / `๐Ÿงน Nitpick` labels are an input to that judgment, not a substitute for it. +- **Fix the class, not the instance** (`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance). CodeRabbit reports the site it read; sweep the siblings across this change's surface and fix them together, then prove the sweep with a search that would fail if one remained. **Do not re-run with a class half-closed.** - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks are immaterial by definition, so do not apply them.** They go in the closing note. Applying one produces a commit, and Step 3 then reruns against it โ€” manufacturing the next pass to change something that changes nothing. If a nitpick turns out to clear the bar, it was never a nitpick: fix it as the blocking finding it is. - **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the tree; when it does not hold, reject the finding with the evidence rather than changing working code to satisfy a misreading. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 2d5f2f5..0fd62f3 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -267,6 +267,7 @@ than waiting it out. immaterial ones โ€” wording, formatting, a count nothing keys on, an entry missing from a list the artifact declares non-exhaustive โ€” go into one closing note and MUST NOT drive another iteration. +- **Fix the class, not the instance** (`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance). Codex reports the site it read; treat it as one instance until you have proved it unique, sweep the lexical, altitude, symmetry and enumeration axes across this change's surface, and fix them in one commit. Prove the sweep with a search whose empty result is the evidence โ€” normalising whitespace, and never filtering out the file under review. **Do not re-run with a class half-closed**: the next pass will spend itself reporting siblings you already knew about. - **Fix real issues** in code and tests; make atomic commits for the fixes. - **Nitpicks are immaterial by definition, so do not apply them.** They go in the closing note. Applying one produces a commit and Step 3 reruns against it, @@ -315,6 +316,11 @@ CONVERGENCE PASS line and the do-not-re-report list are added on top. CONVERGENCE PASS . Prior passes found issues; all fixed except . Do not re-report them. +Classes closed since the last pass โ€” every site of each was swept, not just the +one reported: . Report a further +instance of one of these only if the sweep actually missed it, and say which +site. + Report every BLOCKING finding. A finding is blocking if it is any of: 1. A violation of a rule this project wrote down โ€” anything in AGENTS.md or @@ -333,6 +339,12 @@ Report every BLOCKING finding. A finding is blocking if it is any of: Wording, formatting, and counts nothing keys on are NOT blocking: one closing note, not findings. +When a finding is one instance of a pattern that appears elsewhere, say so and +list every other site you can see. Report it as ONE finding naming the class, +not as one finding per site and not as a single site. A class reported whole is +fixed in one pass; a class reported one instance at a time takes as many passes +as it has members. + Where this artifact declares a list illustrative and a rule authoritative, assess the RULE. A further missing list entry is not a finding. diff --git a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md index 4f289dd..76514fb 100644 --- a/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/copilot-feedback-resolver/SKILL.md @@ -165,6 +165,12 @@ query { For each unresolved Copilot comment: +**Fix the class, not the instance** (`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance). +Copilot comments on the site it read. Before pushing a fix, find every sibling of +that defect within this change's surface and fix them together โ€” otherwise the +next Copilot pass reports them as new findings and the loop spends a full wait +cycle per sibling. Never push with a class half-closed. + **If the coordinator supplied per-thread dispositions, they win.** A disposition from `fx-dev:resolve-pr-feedback` is set with the Scope Brief and the finding ledger in hand; this table classifies from the comment text alone. Apply the diff --git a/plugins/fx-dev/skills/copilot-review/SKILL.md b/plugins/fx-dev/skills/copilot-review/SKILL.md index ddd3d7b..517dbbc 100644 --- a/plugins/fx-dev/skills/copilot-review/SKILL.md +++ b/plugins/fx-dev/skills/copilot-review/SKILL.md @@ -105,6 +105,12 @@ is an **observation, not a finding**, and is **resolved by replying with that reasoning** rather than by editing. The gate is zero *unresolved* Copilot threads, not zero observations acted on. +**Fix the class, not the instance** (`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance). +Copilot reports the site it read; sweep its siblings across this change's surface +and push them as one commit. Here that discipline is worth the most in the loop, +because a push costs a full Copilot wait cycle โ€” closing a class half-way spends +one of those to be told about the other half. + This matters more here than anywhere else in the loop: **every push re-opens the gate.** Copilot must then re-review the new head (Step 5), so editing for an immaterial finding costs a full wait cycle and produces a fresh commit for it to diff --git a/plugins/fx-dev/skills/dev/SKILL.md b/plugins/fx-dev/skills/dev/SKILL.md index 61c4d8b..8b9a126 100644 --- a/plugins/fx-dev/skills/dev/SKILL.md +++ b/plugins/fx-dev/skills/dev/SKILL.md @@ -308,6 +308,8 @@ The Codex CLI takes the scope as its review prompt, so this pass is the one wher #### Remediation and Delta Verification +**Record each finding's class, not just its location** (`references/scope-contract.md` ยง Fix the class, not the instance). A reviewer reports the instance it read; the ledger entry names the defect pattern and every site in the change's surface that exhibits it, so a fix closes the class rather than buying the next cycle its input. A ledger entry is not resolved while a sibling of its class is open, and a reviewer MUST NOT be re-run with a class half-closed. + Fix only findings classified `required-by-contract` or `regression-caused-by-change`. After a fix commit: 1. Rerun the reviewer or check that originated the blocking finding. diff --git a/plugins/fx-dev/skills/dev/references/scope-contract.md b/plugins/fx-dev/skills/dev/references/scope-contract.md index 4170004..aa47ec1 100644 --- a/plugins/fx-dev/skills/dev/references/scope-contract.md +++ b/plugins/fx-dev/skills/dev/references/scope-contract.md @@ -152,6 +152,46 @@ Report the trend when you stop, so the operator can see the shape rather than ta **State honestly what the last pass did not cover.** If you stop after applying fixes that were never themselves reviewed, say so. A loop that stops at the bound below has *not* converged, and reporting it as clean is a false result. +### Fix the class, not the instance + +**This is the single biggest cause of a review loop that will not end, and the bar alone does not prevent it.** A reviewer reports the instance it happened to read. If you fix exactly that instance, the next pass finds a sibling โ€” same defect, different location โ€” and reports it as a new finding. Every pass is then genuinely productive and the loop still never terminates, because the supply of siblings is the size of the class, not the size of the review. + +The failure is invisible from inside a single pass. Each finding is real, each fix is correct, each pass costs a full cycle, and the count drifts sideways forever. Watch for the signature: **findings that are individually valid and collectively repetitive.** That is not the reviewer being pedantic; it is the previous fix having been too small. + +**A finding is an instance of a class until you have proved it unique.** Before fixing, name the class โ€” the defect *pattern*, stated without reference to any location โ€” then find every site in this change's surface that exhibits it, and fix them together in one commit. + +#### The sibling axes + +A class rarely lives at one altitude or in one file. Check each axis before declaring a sweep complete: + +| Axis | The question | Typical miss | +|---|---|---| +| **Lexical** | Where else does this exact wording appear? | A phrase fixed at one site, unchanged at eleven others | +| **Altitude** | Does this rule also appear as a summary, a table row, a step-by-step handler, a checklist, a success criterion, an example, a template, or a command block? | The definition is corrected; the procedure that implements it still describes the old behaviour, and a reader who follows the steps gets the old rule | +| **Symmetry** | Is there a counterpart file or branch โ€” reviewer A and reviewer B, mode 1 and mode 2, the parallel resolver? | One of a near-identical pair is fixed and the twin is untouched | +| **Enumeration** | If one member of a list was wrong, are the others? | A list gains a missing case at the site reported and keeps the same gap everywhere else it is restated | + +#### Prove the sweep with a search that would fail + +**A sweep is not complete because it felt complete.** End it with a search whose *empty result* is the evidence, and read that result. Two ways this has genuinely gone wrong, both of which reported success: + +- **A line-oriented search against prose that wraps.** The phrase existed, split across a newline, and `grep` could not see it. Normalise whitespace before searching for anything longer than a few words. +- **A search that excluded the file under review.** A filter meant to trim noise removed the one remaining site, and the empty output was read as proof. + +If the verifying search cannot fail, it is not verifying anything. State the search you ran alongside the claim that the class is closed, so a wrong one is visible rather than trusted. + +#### When the class is large, the duplication is the defect + +Past a handful of sites, stop syncing copies and remove the need for them: define the rule once and have every other site reference it (ยง Blocking is the worked example). Fixing eleven copies leaves eleven copies to drift again on the next change. This is a design decision, so if it is not obviously right, raise it rather than performing a large mechanical rewrite unasked. + +#### Scope still binds + +Sweeping a class is **not** a licence to widen the change. The class is bounded by this change's blast radius: the files it touches, plus files it made inconsistent (`regression-caused-by-change`). A sibling outside that radius is a deferred follow-up, reported and not fixed โ€” the sprawl stop rule governs here exactly as elsewhere. + +#### Close the class before re-running + +**Do not start the next review pass with a class half-closed.** A re-run against a partial fix spends a full cycle to be told about the siblings you already knew about, and its findings are indistinguishable from new ones. Finish the sweep, verify it, and record the class as closed โ€” then carry that into the next pass's prompt so the reviewer spends the pass on ground nobody has covered. + ### The iteration bound **Convergence is the goal. The bound is a runaway backstop, not a target, and reaching it is a failure to converge โ€” never a stopping condition you are entitled to treat as success.** diff --git a/plugins/fx-dev/skills/pr-reviewer/SKILL.md b/plugins/fx-dev/skills/pr-reviewer/SKILL.md index 2e0d04a..eb18407 100644 --- a/plugins/fx-dev/skills/pr-reviewer/SKILL.md +++ b/plugins/fx-dev/skills/pr-reviewer/SKILL.md @@ -68,6 +68,12 @@ noise the author learns to skim: - a limit the change admits and gates ("verified at implementation time", "open question gated on X") โ€” check the gate is real and sequenced, and move on. +**When a finding is one instance of a pattern, say so and list the other sites** +(`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance). +A reviewer that reports one occurrence of a defect it can see in eleven places +has handed the author ten future review cycles. Name the class, enumerate what +you found, and count it as one finding โ€” not eleven, and not one. + **A review that reports twenty things equally has reported nothing.** State the count at each tier so the author knows what to act on first. diff --git a/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md b/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md index bb75f44..cf432b1 100644 --- a/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md +++ b/plugins/fx-dev/skills/rabbit-feedback-resolver/SKILL.md @@ -168,6 +168,12 @@ query { For each unresolved CodeRabbit comment: +**Fix the class, not the instance** (`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance). +CodeRabbit comments on the site it read. Before pushing a fix, find every sibling +of that defect within this change's surface and fix them together โ€” otherwise the +next pass reports them as new findings and the loop never ends. Never push with a +class half-closed. + **If the coordinator supplied per-thread dispositions, they win.** A disposition from `fx-dev:coderabbit-review` or `fx-dev:resolve-pr-feedback` is set with the Scope Brief and the finding ledger in hand; this table classifies from comment diff --git a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md index 309db52..6a575af 100644 --- a/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md +++ b/plugins/fx-dev/skills/resolve-pr-feedback/SKILL.md @@ -27,6 +27,8 @@ An in-scope item that would change nothing if it shipped uncorrected โ€” wording This matters most in the loop below. Each fix push triggers another reviewer pass, so actioning immaterial findings does not converge โ€” it manufactures the next round's input. Push fixes for blocking findings (`fx-dev/skills/dev/references/scope-contract.md` ยง Blocking); reply-and-resolve only the immaterial rest in the same cycle. A contract blocker is never discharged by a reply explaining it: the artifact has to change. +**Fix the class, not the instance.** A reviewer reports the site it read. Fixing exactly that site means the next pass finds its siblings and reports them as new findings โ€” every pass productive, the loop never ending. Sweep the class across this change's surface before pushing (`fx-dev/skills/dev/references/scope-contract.md` ยง Fix the class, not the instance), and do not trigger another reviewer pass with a class half-closed: each push buys a full cycle, and spending one to be told about siblings you already knew about is the most expensive way to make no progress. + **Verify before fixing.** A finding's premise can be wrong. Check any claim it makes about the code, and when it does not hold, reply with the evidence and resolve rather than changing working code to satisfy a misreading. ## WHEN TO USE THIS SKILL From 4aef125deed7c15b249cb7b470f339e4ca3577c2 Mon Sep 17 00:00:00 2001 From: Marian Rudzynski Date: Sat, 15 Aug 2026 22:28:58 +0000 Subject: [PATCH 29/48] fix(fx-dev): close nine gaps codex pass 1 found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex's first pass on the materiality-bar branch found nine, and all nine were real. Three were the same shape: a rule stated correctly at one altitude and left describing the old behaviour at another. The rabbit resolver was the unswept twin of the copilot one. Its General Feedback handler still said "if valid: delegate to coder", so an agent reaching it would edit and push for an immaterial finding -- the exact loop the bar exists to stop -- and it had no immaterial branch at all. Its deferred path told the agent to load project-management and track the follow-up, one step above a paragraph forbidding any commit, and its success criteria and summary table still required the PROJECT.md entry the new procedure forbids. The copilot twin already had all of this; rabbit now matches it. Three coordinators mandate a disposition per thread and two could not produce one. resolve-pr-feedback's query returned id, isResolved and the first author's login -- no body, path or line -- so the dispatch step it feeds had nothing to run the filters over; the query now fetches them. coderabbit-review's Mode 2 had only the waiter's count between the wait and the dispatch, so it gains a fetch-and-triage step. copilot-review never fetches threads at all, so it now passes dispositions only for the suppressed items it did read and leaves the rest to the resolver, rather than inventing authoritative ones. The reviewer's half of "fix the class" was defined in two places and canonically in neither: pr-reviewer stated it, the Codex prompt stated it independently, and the contract defined only the fixer's sweep. It is now canonical as ยง Reporting a class, pr-reviewer references it, and the Codex block is marked as mirroring it. Also: the class bound said "the files this change touches", which is circular when the request is itself class-wide -- a file not yet touched is inside the brief, and deferring it leaves the requested work unfinished. The bound is the Scope Brief's interpreted scope, with the blast radius as what that means when the request did not ask to reach further. The Codex mirror required a list to be *declared* illustrative where the canonical rule also accepts "for example", so a narrower mirror could resume the N+1 churn. And resolve-pr-feedback said each push "triggers" another reviewer pass, which is true of CodeRabbit and false of Copilot -- readable as permission to wait for a rerun that never comes. Deferred, out of scope: team/SKILL.md invokes copilot-review and coderabbit-review with a bare PR number, losing the brief. It predates this branch and the file is not part of this change. --- .claude-plugin/marketplace.json | 2 +- plugins/fx-dev/.claude-plugin/plugin.json | 2 +- .../fx-dev/skills/coderabbit-review/SKILL.md | 38 ++++++++++++++++++- plugins/fx-dev/skills/codex-review/SKILL.md | 13 ++++--- plugins/fx-dev/skills/copilot-review/SKILL.md | 17 ++++++++- plugins/fx-dev/skills/dev/SKILL.md | 4 +- .../skills/dev/references/scope-contract.md | 16 +++++++- plugins/fx-dev/skills/pr-reviewer/SKILL.md | 8 ++-- .../skills/rabbit-feedback-resolver/SKILL.md | 26 ++++++++----- .../skills/resolve-pr-feedback/SKILL.md | 14 ++++++- 10 files changed, 109 insertions(+), 31 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0370ef4..79c8e9e 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.25.1", "homepage": "https://cc.fx.gd" }, "plugins": [ diff --git a/plugins/fx-dev/.claude-plugin/plugin.json b/plugins/fx-dev/.claude-plugin/plugin.json index 524bd75..730de1f 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": "3.12.0", + "version": "3.12.1", "description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration" } diff --git a/plugins/fx-dev/skills/coderabbit-review/SKILL.md b/plugins/fx-dev/skills/coderabbit-review/SKILL.md index 6b2a1d0..7d13041 100644 --- a/plugins/fx-dev/skills/coderabbit-review/SKILL.md +++ b/plugins/fx-dev/skills/coderabbit-review/SKILL.md @@ -153,9 +153,43 @@ Script exit codes: - **Exit 2**: No CodeRabbit check present after a one-cycle grace period โ†’ the CodeRabbit GitHub App is not configured for this repo. Report once and proceed without the PR-level gate. - **Exit 3**: Invalid arguments or gh error โ†’ report error to user. If the error specifically identifies a CodeRabbit rate/quota limit, report once and proceed without CodeRabbit. +### Step 1b: Fetch the Threads and Assign a Disposition to Each + +**The waiter emits a count, not the threads.** Step 2 hands the resolver a +disposition per thread, and a count cannot be triaged โ€” so fetch the thread +bodies first and run the filters over them yourself (scope, then contract, then +materiality: `fx-dev/skills/dev/references/scope-contract.md` ยง Three filters). +Skipping this leaves only two options at Step 2, and both are wrong: invent +dispositions, or invoke the resolver bare and let it re-derive triage it cannot +see. + +```bash +# Replace OWNER, REPO, PR_NUMBER with actual values (GraphQL body โ€” no shell expansion here) +gh api graphql -f query=' +query { + repository(owner: "OWNER", name: "REPO") { + pullRequest(number: PR_NUMBER) { + reviewThreads(first: 100) { + nodes { + id + isResolved + path + line + comments(first: 10) { nodes { author { login } body } } + } + } + } + } +}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and (.comments.nodes[0].author.login | tostring | contains("coderabbitai")))]' +``` + +Assign each thread exactly one of `blocking`, `immaterial`, or `deferred` +(`fx-dev/skills/dev/references/scope-contract.md` ยง Resolver dispositions). Yours +is authoritative โ€” you hold the Scope Brief; the resolver does not. + ### Step 2: Resolve Feedback -If the script reports unresolved CodeRabbit threads (count > 0), invoke the rabbit-feedback-resolver: +If the script reports unresolved CodeRabbit threads (count > 0), invoke the rabbit-feedback-resolver with the dispositions from Step 1b: ``` Skill tool: skill="fx-dev:rabbit-feedback-resolver", @@ -168,7 +202,7 @@ That skill handles per-thread categorisation, pushes any code fixes, replies, an CodeRabbit re-reviews after every push. Once Step 2 pushes fixes, the `CodeRabbit` check goes pending again โ€” go back to Step 1. -**Repeat Steps 1 โ†’ 2 until BOTH hold:** +**Repeat Steps 1 โ†’ 1b โ†’ 2 until BOTH hold:** 1. The most-recent CodeRabbit check is in a terminal state with conclusion `success` (or `skipped` / `neutral` if the repo configures it that way). 2. Re-querying review threads shows 0 unresolved CodeRabbit threads. diff --git a/plugins/fx-dev/skills/codex-review/SKILL.md b/plugins/fx-dev/skills/codex-review/SKILL.md index 0fd62f3..917ce50 100644 --- a/plugins/fx-dev/skills/codex-review/SKILL.md +++ b/plugins/fx-dev/skills/codex-review/SKILL.md @@ -307,10 +307,10 @@ of low-value findings, so omitting it there costs the most; on re-runs the CONVERGENCE PASS line and the do-not-re-report list are added on top. > **This block is a MIRROR** of `fx-dev/skills/dev/references/scope-contract.md` -> ยง Blocking and ยง Three things that are not findings โ€” the one case the -> define-once rule exempts, because `codex review` receives a string and cannot -> follow a link. Keep it a faithful restatement, never an independent edit, and -> update it in the same commit that changes the canonical text. +> ยง Blocking, ยง Reporting a class, and ยง Three things that are not findings โ€” the +> one case the define-once rule exempts, because `codex review` receives a string +> and cannot follow a link. Keep it a faithful restatement, never an independent +> edit, and update it in the same commit that changes the canonical text. ``` CONVERGENCE PASS . Prior passes found issues; all fixed except