fix(rules): stop false-positive counterfeiter and Parse*Default findings - #115
Conversation
github-pr-reviewer re-reported present code as missing across three review rounds on bborbe/github-update-go-watcher PR #1. Root cause: two mechanical rules firing on compliant code. - counterfeiter-directive-on-interface: not.follows only sees the immediate previous sibling, so a doc comment between the directive and the interface masked the directive and fired on every compliant interface. Converted to the over-inclusive + adjudicate design used by its sibling rules (counterfeiter-mocks-required, main-test-with- compiles, external-call-logs-response): the agent verifies the comment block above the interface before reporting. - new-prefix-constructor-naming: fired on Parse<X>Default functions, contradicting paired-parse-and-parsedefault which mandates that exact name. Added a Parse[A-Z].*Default exemption. Both rules get ast-grep rule-tests; index and docs regenerated.
There was a problem hiding this comment.
No cleanup needed — already at PR head. Here is my consolidated report:
PR Review: fix(rules): stop false-positive counterfeiter and Parse*Default findings
Must Fix (Critical)
None.
Should Fix (Important)
rules/index.json— The entry forgo-architecture/counterfeiter-directive-on-interfacestill carries"enforcement_type": "mechanical". The rule's new design is over-inclusive by design (flags every exported interface) and delegates the false-positive suppression entirely to the agent reading the comment block above the interface. This is a judgment-tier pattern, not a mechanical one. The index entry should reflect"enforcement_type": "judgment"(or equivalent) so downstream dispatch logic does not skip the adjudicator. The YAML rule frontmatter (rules/go/counterfeiter-directive-on-interface.yml) has noenforcement_typefield — it may need one added so the index regenerates correctly.
Nice to Have (Optional)
rule-tests/__snapshots__/go-architecture/counterfeiter-directive-on-interface-snapshot.yml— The snapshot file holds byte/char offsets for AST label ranges. For the first snapshot (no-directive case), manual character-count verification of the snippet suggests theprimarylabel'sstart: 72may be off by one relative to thetypekeyword position in the snippet (// UserService is...→type Service…→ closing}). This warrants a single test run withRERUN_SNAPSHOTS=1or equivalent to confirm all offsets are accurate and the snapshot tests pass after the redesign. The diff shows this file was newly added, so no prior baseline exists.CHANGELOG.md:1— Mechanical finding: no## Unreleasedbullet present. In an autoRelease repo the release agent promotes## Unreleasedpost-merge; without one, no version ships for this PR. Add a conventional-prefixed bullet under## Unreleasedbefore merging. (rule:changelog/unreleased-entry-required)
Notes
The two rule changes are directionally correct:
counterfeiter-directive-on-interface— Switching from a strictnot.followssibling check to an over-inclusive anchor + adjudicator is the right call. The//counterfeiter:generatedirective legitimately sits above multi-line doc comment blocks on real interfaces, and ast-grep's immediate-sibling relations cannot bridge that gap. The YAML test file (rule-tests/counterfeiter-directive-on-interface-test.yml) covers the three key cases correctly: (a) no directive → flagged, (b) directive one line above → not flagged, (c) directive above doc comment → not flagged. Theignoreslist (main.go,**/mocks/**, test files) is appropriate.new-prefix-constructor-naming— Adding theParse[A-Z].*Default$regex exemption is correct. The pattern^Parse[A-Z].*Default$correctly matchesParseConfigDefault,ParseGzipDefault, etc. and correctly excludesParseHTTPScheme(noDefaultsuffix). The test added inrule-tests/new-prefix-constructor-naming-test.ymlvalidates this. The alternation in the YAML (not: any: - regex: '^New' - regex: '^Parse[A-Z].*Default$') is the right ast-grep shape.
Traceability: This review used the pre-computed mechanical findings from ast-grep-runner (1 finding — CHANGELOG), the Step 0c diff, and manual inspection of all 7 changed files. The selector-mode guide (docs/selector-mode-guide.md) was inaccessible in this environment, so the judgment-tier adjudication was performed in-session rather than via the guide's structured classify/adjudicate steps.
Verdict: request-changes — One Should Fix (index.json enforcement_type mismatch) and one Should Fix (CHANGELOG missing ## Unreleased). The rule design is sound, but the index metadata and changelog must be corrected before merge.
{
"verdict": "request-changes",
"summary": "The rule redesigns are correct and well-tested, but the index.json entry for counterfeiter-directive-on-interface still carries enforcement_type: mechanical despite being judgment-tier by design, and the CHANGELOG is missing its ## Unreleased section in an autoRelease repo.",
"comments": [
{
"file": "rules/index.json",
"line": 244,
"severity": "major",
"message": "go-architecture/counterfeiter-directive-on-interface has enforcement_type: 'mechanical' but the rule's new design is over-inclusive anchor + agent adjudicator (judgment-tier). The index entry must reflect 'judgment' so downstream dispatch does not skip the adjudicator. Add enforcement_type to the YAML frontmatter so it regenerates correctly."
},
{
"file": "CHANGELOG.md",
"line": 1,
"severity": "major",
"message": "PR changes source but CHANGELOG.md has no '## Unreleased' bullet. In an autoRelease repo the release agent promotes '## Unreleased' post-merge; with none, no version ships. Add a conventional-prefixed bullet under '## Unreleased' before merging. See docs/changelog-guide.md."
},
{
"file": "rule-tests/__snapshots__/go-architecture/counterfeiter-directive-on-interface-snapshot.yml",
"line": 1,
"severity": "minor",
"message": "Snapshot holds byte-offset label positions. Manual count on the first snapshot (no-directive case) suggests start:72 may be off by one relative to the type keyword position in the snippet. Recommend running the snapshot tests (RERUN_SNAPSHOTS=1 or equivalent) to confirm all offsets are accurate after this redesign."
}
],
"concerns_addressed": [
"correctness: Rule switched to over-inclusive anchor + adjudicator — design verified sound; YAML test cases cover all three key scenarios",
"correctness: ParseDefault exemption regex verified — ^Parse[A-Z].*Default$ correctly matches ParseConfigDefault and excludes non-Default Parse functions",
"tests: Snapshot file added — offsets cannot be fully verified without running tests; flagged for confirmation",
"correctness: index.json enforcement_type mismatch flagged — counterfeiter rule is judgment-tier but indexed as mechanical"
]
}- go-architecture-patterns.md Enforcement line now mirrors the sibling counterfeiter-mocks-required rule: mechanical first-pass + judgment-tier LLM adjudication (index regenerated). enforcement_type stays mechanical — the YAML genuinely runs in the funnel (selected by enforcement regex); the adjudication is explicit in the doc and enforced by the review prompt. - CHANGELOG: add ## Unreleased section (repo has release.autoRelease: true; without it no version ships post-merge).
superseded by new automated review
There was a problem hiding this comment.
The mechanical funnel ran with zero findings and both changed rules are enforcement_type: mechanical (not judgment-tier), so the Step 4b-i candidate set is empty. Per selector-mode short-circuit: with no applicable judgment rules and no mechanical findings, I can proceed directly to Step 5.
PR Review: fix(rules): stop false-positive counterfeiter and Parse*Default findings
Branch:fix/pr-reviewer-false-positives → master
Files changed: 8 files, +206 / −29 lines
Must Fix (Critical)
None.
Should Fix (Important)
None.
Nice to Have (Optional)
None.
Selector Mode Traceability
- Candidates: 0 judgment rules matched by Step 4b-i glob filter
- Applicable: 0 (candidate set was empty; both changed rules are
enforcement_type: mechanical, not judgment) - Skipped: N/A — no candidates to evaluate
- Mechanical funnel: 0 findings across 74 YAMLs (already confirmed clean by workflow)
- Step 4d-sel: skipped — short-circuit condition met (no candidates + no mechanical findings)
Verdict
The diff is a targeted bugfix to two ast-grep rules:
counterfeiter-directive-on-interface.yml— removed thenot.followsfilter (which missed directives separated from interfaces by doc comments) and replaced it with an over-inclusive anchor that flags every exported interface; LLM adjudication now handles the false-positive dismissal. The mechanical funnel correctly returns zero findings for this rule.new-prefix-constructor-naming.yml— addedParse[X]Defaultexemption to avoid contradicting thego-parse/paired-parse-and-parsedefaultrule. The regex^Parse[A-Z].*Default$is sound.
Both changes are mechanically correct, the test fixtures and snapshots are consistent, and the docs accurately describe the new adjudication design. No issues found.
{
"verdict": "approve",
"summary": "Bugfix changing two ast-grep rules from strict mechanical checks to over-inclusive anchors with LLM adjudication. counterfeiter-directive-on-interface now flags all exported interfaces and lets the adjudicator confirm directive presence; new-prefix-constructor-naming exempts Parse<X>Default to avoid contradicting paired-parse-and-parsedefault. Mechanical funnel confirms zero findings; test fixtures and snapshots are consistent.",
"comments": [],
"concerns_addressed": [
"correctness: counterfeiter rule now uses judgment-tier adjudication to handle doc-comment-separated directives — no false positives expected",
"correctness: Parse<X>Default exemption regex is sound (^Parse[A-Z].*Default$) — no contradiction with paired-parse-and-parsedefault",
"correctness: over-inclusive anchor acknowledged in rule message and docs — adjudication design is explicit",
"tests: new rule-test covers all key cases (unexported, struct, exported without directive, exported with directive, exported with directive above multi-line doc comment)",
"tests: ParseConfigDefault valid case added to new-prefix-constructor-naming test"
]
}Uh oh!
There was an error while loading. Please reload this page.
What
Fixes two mechanical ast-grep rules that fire on compliant code, the root cause of the github-pr-reviewer re-reporting present code as missing across three review rounds on
bborbe/github-update-go-watcherPR #1.Changes
counterfeiter-directive-on-interface— the old rule usednot.follows: {kind: comment, regex: counterfeiter:generate}, but ast-grep'sfollowsrelation only reaches the immediate previous sibling. Real Go layout isdirective → doc comment → interface, so the doc comment masked the directive and the rule fired on every compliant interface. Converted to the over-inclusive + adjudicate design used by sibling rules: the agent verifies the comment block above the interface before reporting. Added rule-test.new-prefix-constructor-naming— fired onParse<X>Defaultfunctions, directly contradictingpaired-parse-and-parsedefaultwhich mandates that exact name. AddedParse[A-Z].*Defaultexemption. Added rule-test.rules/index.jsonregenerated.Verification
ast-grep test -c sgconfig.yml— 46 passed, 0 failed (new tests for both rules)make precommit— passPair with
bborbe/github-pr-review-agent#(prompt-side adjudication fix).