Skip to content

v0.3.1#8

Merged
mattyhansen merged 17 commits into
mainfrom
dev
Jun 8, 2026
Merged

v0.3.1#8
mattyhansen merged 17 commits into
mainfrom
dev

Conversation

@mattyhansen

@mattyhansen mattyhansen commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Note

High Risk
Relocates security-sensitive PreToolUse/PostToolUse hooks and rewrites paths every goat skill depends on; a bad root resolve or missing .goat-flow/hooks install would weaken agent guardrails or break workflows.

Overview
goat-flow 1.10.1 recenters agent policy on .goat-flow/: shared docs move to skill-docs/, learning artifacts to learning-loop/, milestone coordination to plans/, and review scratch output to logs/review/. All goat skills in .agents/, .claude/, and mirrored copies bump to 1.10.1 with path and behavior updates (e.g. /goat-plan writes under .goat-flow/plans/, footgun grep targets learning-loop/).

Hooks are no longer duplicated under .claude/hooks/; Claude, Codex, and new .agents/hooks.json invoke .goat-flow/hooks/deny-dangerous.sh and gruff-code-quality.sh via richer git-root resolution (submodules, CLAUDE_PROJECT_DIR fallback). Claude drops redundant MultiEdit secret denies and the MultiEdit gruff hook; Codex points at the same canonical scripts and tightens filesystem denies in config.toml.

Skill protocol tweaks include stricter /goat-debug quick-path evidence, /goat-review optional fetch (base-fetch-skipped), refuter artifacts under logs/review/, ship verdict PENDING REFUTER/HUMAN, /goat-qa continuing to Phase 3 on explicit test-plan intent, /goat-security summarizing withheld leads, and /goat routing code exploration to Investigate mode.

Reviewed by Cursor Bugbot for commit ef99d2a. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Advisory rule to flag redundant static-existence assertions in same-file tests.
    • Analyzer changed-region delegation and a self-test path to validate changed-range reporting.
  • Bug Fixes

    • More robust repository-root discovery and safer analyzer timeout/exit handling.
    • Improved ignore/out-of-scope messages and staged-change file discovery.
  • Improvements

    • Severity-ranked, floored-and-capped reporting with consistent suppressed/“more” summaries.
    • Expanded file-extension support and Symfony YAML route indexing for dead-code checks.
  • Documentation

    • Rule count bumped 132→133; updated changelog, docs and reporting format notes.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes.

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds StaticAnalysisRedundantTestRule; refactors gruff-code-quality hooks for jq-first multi-path discovery, native changed-region delegation, and severity-ranked JSON reporting; extends DeadCode YAML _controller indexing; and updates reporters, tests, fixtures, docs, and hook wiring.

Changes

Static-analysis-redundant-test rule and hook script refactoring

Layer / File(s) Summary
Payload and scope extraction refactoring
.claude/hooks/gruff-code-quality.sh, .codex/hooks/gruff-code-quality.sh
Multi-path jq-first JSON extraction (json_file_paths) replaces single-path logic; regex fallback (fallback_file_paths) supports jq-less environments; staged changes are included via git diff --cached; gruff-ts extension mapping expanded to mts/cts/mjs/cjs.
Severity ranking and findings reporting
.claude/hooks/gruff-code-quality.sh, .codex/hooks/gruff-code-quality.sh
min_severity_rank maps env-configurable floors; changed_findings_report builds structured JSON with severity-sorted, capped listings and counts; native_suppressed_count reads analyzer suppression when available; ignore descriptors are merged; print_scope_header standardizes headers.
Analyzer invocation and timeout handling
.claude/hooks/gruff-code-quality.sh, .codex/hooks/gruff-code-quality.sh
run_gruff_json detects native changed-region support and passes --no-baseline --changed-ranges --changed-scope symbol when supported; validates GRUFF_CODE_QUALITY_TIMEOUT_SECONDS and treats common kill/timeout exit codes specially.
Process file orchestration with new reporting
.claude/hooks/gruff-code-quality.sh, .codex/hooks/gruff-code-quality.sh
process_file uses native-regions detection, invokes run_gruff_json(binary,ranges), falls back .yaml.yml, includes exit status in JSON validation diagnostics, replaces filter_findings with changed_findings_report, and selects suppressed-count source based on native mode.
Hook self-testing and main arg handling
.claude/hooks/gruff-code-quality.sh, .codex/hooks/gruff-code-quality.sh
Adds self_test() exercising jq, payload/range parsing, severity ordering, caps/flooring, and native-vs-fallback scoping; main() accepts --self-test=smoke.
Documentation and runtime contracts
.claude/hooks/gruff-code-quality.sh, .codex/hooks/gruff-code-quality.sh
Comments updated to reflect multi-config support, expanded extensions, native scoping ownership, and the new JSON reporting contract.
Settings git root detection and failure handling
.claude/settings.json
Use git rev-parse --show-toplevel; PreToolUse now blocks with exit 2 if root unavailable; PostToolUse hooks skip gracefully with exit 0 when root cannot be determined.
StaticAnalysisRedundantTestRule implementation
src/Rule/TestQuality/StaticAnalysisRedundantTestRule.php
New advisory rule indexes same-file class-like declarations and members, detects assertTrue-wrapped existence checks resolved via ::class or member literals, and emits advisory findings for statically provable redundant assertions.
Rule registration and fixture setup
src/Rule/RuleRegistry.php, tests/Fixtures/TestQuality/static-analysis-redundant-test.php
Registers the new rule in defaults and adds a fixture with classes, interface, trait, enum, factory, and PHPUnit tests exercising redundant and behavioral assertions.
Rule test coverage and snapshot updates
tests/Rule/RuleRegistryTest.php, tests/Rule/RuleRegressionSnapshotTest.php, tests/Rule/TestQuality/TestQualityRulesTest.php
Adds tests and providers validating redundant candidates, metadata (variant/evidenceSymbol), severity/confidence, non-overlap with neighboring rules, and updates registry/regression snapshots and hashes.
Text reporter and summary alignment
src/Reporting/TextReporter.php, src/Command/SummaryCommand.php
TextReporter emits tool/version, conditional Composite line, and a consolidated Findings totals line at the top; SummaryCommand formatting updated to match.
Golden fixtures and CLI tests
tests/Fixtures/Cli/Golden/*, tests/Console/*
Golden text/JSON fixtures and CLI tests updated to expect version 0.3.1 and the new header/findings layout; tests strengthened to assert absence of per-finding lines.
Dead-code YAML indexing and tests
src/Rule/DeadCode/DeadCodeProjectIndex.php, src/Rule/ProjectSourceTextRuleAccumulator.php, src/Rule/DeadCode/UnusedInternalClassRule.php, tests/Fixtures/DeadCode/..., tests/Rule/DeadCode/ProjectDeadCodeRulesTest.php
DeadCodeProjectIndex parses Symfony YAML _controller entries for FQCN::method shapes, records class references, adds a marker interface for project-source-text accumulators, updates UnusedInternalClassRule signature to implement it, and adds YAML route fixtures plus tests asserting referenced controllers remain live.
Diff suppressedCount and legacy pipeline filtering
src/Diff/DiffResult.php, src/Command/AnalyseCommand.php, src/Command/AnalysisFindingSupport.php, src/Command/AnalysisPipeline.php, src/Command/BranchReviewBuilder.php
DiffResult carries suppressedCount; AnalyseCommand persists it; AnalysisFindingSupport adds filterProjectRuleFindingsToFiles; AnalysisPipeline/BranchReviewBuilder use sourceFilePaths and apply filtering to project-rule findings when CLI paths are specified.
Analyse CLI changed-ranges tests
tests/Console/AnalyseCliDiffTest.php
Adds tests and helpers to validate changed-ranges reconciliation for intra-file and project-wide scenarios and asserts suppressed/diff-suppressed counts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • blundergoat/gruff-php#7: The main PR’s gruff-code-quality hook refactors its JSON parsing and changed-region scoping to consume/report analyzer suppressedCount/out-of-scope behavior, which aligns with the retrieved PR’s core changes to suppressedCount and changed-ranges reporting contracts in the CLI analysis pipeline.
  • blundergoat/gruff-php#5: Both PRs touch the CLI version surface area by updating src/Console/Application.php::VERSION and the corresponding CLI tests/fixtures to expect the new gruff-php version string; they’re otherwise unrelated to each PR’s core logic changes.

In burrowed code I nibbled through the churn,
Found assertions redundant where symbols already turn.
Hooks gather paths from streams both wide and sly,
Rank findings, cap their lists, and let suppressed counts fly —
A rabbit hops, reports in JSON bright, and grins. 🐇✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Comment thread .goat-flow/hooks/gruff-code-quality.sh
Comment thread .claude/hooks/gruff-code-quality.sh Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f55f5ee263

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return null;
}

$declaredName = $declaration[$memberBucket][strtolower($member)] ?? null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve property-name case in redundant-test matching

For property_exists() candidates this lower-cases the asserted member name before lookup. PHP property names are case-sensitive, so property_exists(Foo::class, 'LABEL') is false when the declaration is only $label; with the new rule enabled, that typo/case regression is reported as a redundant static-fact assertion and can lead users to remove the test instead of fixing the property name. Keep method lookups case-insensitive, but compare properties with their original case.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.codex/hooks/gruff-code-quality.sh (1)

1-857: 💤 Low value

Consider extracting shared hook logic to reduce duplication.

This file is nearly identical to .claude/hooks/gruff-code-quality.sh. Both scripts must be updated in lockstep, which creates maintenance risk. A shared script (e.g., in a common location like bin/ or .goat-flow/hooks/) sourced or symlinked by both could reduce drift.

That said, separate copies may be intentional for runtime isolation—defer if so.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.codex/hooks/gruff-code-quality.sh around lines 1 - 857, The hook is
duplicated with .claude/hooks/gruff-code-quality.sh; extract the common logic
into a single sourced module (e.g., bin/gruff-code-quality-common.sh) and have
both scripts source it. Move shared functions and symbols such as read_stdin,
json_file_paths, variant_for_path, discover_binary, changed_ranges,
run_gruff_json, valid_gruff_json, changed_findings_report, suppressed_count,
ignored_descriptor, process_file, and main-support helpers into the common file
and leave only small wrapper scripts that set any runner-specific constants and
call the shared main/process_file entrypoint; update both
.codex/hooks/gruff-code-quality.sh and .claude/hooks/gruff-code-quality.sh to
source the common file and delegate to the shared functions so future fixes are
made once.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.codex/hooks/gruff-code-quality.sh:
- Around line 1-857: The hook is duplicated with
.claude/hooks/gruff-code-quality.sh; extract the common logic into a single
sourced module (e.g., bin/gruff-code-quality-common.sh) and have both scripts
source it. Move shared functions and symbols such as read_stdin,
json_file_paths, variant_for_path, discover_binary, changed_ranges,
run_gruff_json, valid_gruff_json, changed_findings_report, suppressed_count,
ignored_descriptor, process_file, and main-support helpers into the common file
and leave only small wrapper scripts that set any runner-specific constants and
call the shared main/process_file entrypoint; update both
.codex/hooks/gruff-code-quality.sh and .claude/hooks/gruff-code-quality.sh to
source the common file and delegate to the shared functions so future fixes are
made once.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d31c1fa3-5a4e-475b-92e0-84c53599b75a

📥 Commits

Reviewing files that changed from the base of the PR and between 17baeae and f55f5ee.

📒 Files selected for processing (19)
  • .claude/hooks/gruff-code-quality.sh
  • .claude/settings.json
  • .codex/hooks/gruff-code-quality.sh
  • .goat-flow/architecture.md
  • .goat-flow/code-map.md
  • .goat-flow/decisions/ADR-022-test-quality-gate-parity.md
  • .gruff-php.yaml
  • composer.json
  • docs/rules.md
  • src/Command/SummaryCommand.php
  • src/Reporting/TextReporter.php
  • src/Rule/RuleRegistry.php
  • src/Rule/TestQuality/StaticAnalysisRedundantTestRule.php
  • tests/Console/GruffCliSummaryTest.php
  • tests/Fixtures/Cli/Golden/text-warning.txt
  • tests/Fixtures/TestQuality/static-analysis-redundant-test.php
  • tests/Rule/RuleRegistryTest.php
  • tests/Rule/RuleRegressionSnapshotTest.php
  • tests/Rule/TestQuality/TestQualityRulesTest.php

Comment thread .claude/hooks/gruff-code-quality.sh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6b30ae7e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +125 to +128
foreach (NodeIndex::nodesOfAny(
$analysisUnit,
[Stmt\Class_::class, Stmt\Interface_::class, Stmt\Trait_::class, Stmt\Enum_::class],
) as $node) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip non-top-level declarations for redundant checks

When a test file contains a class/interface/trait/enum declared inside a function, method, or conditional block, PHP does not register that symbol until that code path executes; for example class_exists(Foo::class, false) before a nested declaration is still false. Because this indexes every class-like node returned by the full-AST NodeIndex walk, the new rule treats those runtime-dependent declarations as statically proven and can report a non-redundant existence assertion as something users should remove. Restrict the declaration index to top-level/namespace declarations, or otherwise exclude conditional/nested declarations.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/Console/GruffCliSummaryTest.php (1)

40-40: ⚡ Quick win

Use Application::VERSION instead of hardcoded release strings in assertions.

This avoids touching multiple tests on every version bump.

Proposed refactor
+use GruffPhp\Console\Application;
 use JsonException;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Process\Process;
@@
-        self::assertStringContainsString('gruff-php 0.3.1 summary', $output);
+        self::assertStringContainsString('gruff-php ' . Application::VERSION . ' summary', $output);
@@
-        self::assertSame('0.3.1', $tool['version'] ?? null);
+        self::assertSame(Application::VERSION, $tool['version'] ?? null);

Also applies to: 111-111

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Console/GruffCliSummaryTest.php` at line 40, Replace the hardcoded
version string in the assertion with the app's version constant: instead of
asserting 'gruff-php 0.3.1 summary', build the expected text using
Application::VERSION (e.g., "gruff-php " . Application::VERSION . " summary")
and use that in the assertStringContainsString call referring to $output; update
all similar assertions (e.g., the one at line 111) so tests no longer rely on a
pinned release string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/Console/GruffCliSummaryTest.php`:
- Line 40: Replace the hardcoded version string in the assertion with the app's
version constant: instead of asserting 'gruff-php 0.3.1 summary', build the
expected text using Application::VERSION (e.g., "gruff-php " .
Application::VERSION . " summary") and use that in the
assertStringContainsString call referring to $output; update all similar
assertions (e.g., the one at line 111) so tests no longer rely on a pinned
release string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f2bb75c-576e-4501-8e8d-d21666e03f14

📥 Commits

Reviewing files that changed from the base of the PR and between f55f5ee and f6b30ae.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • CHANGELOG.md
  • src/Console/Application.php
  • src/Rule/DeadCode/DeadCodeProjectIndex.php
  • src/Rule/DeadCode/UnusedInternalClassRule.php
  • src/Rule/ProjectSourceTextRuleAccumulator.php
  • src/Rule/RuleRegistry.php
  • tests/Console/AnalyseCliTest.php
  • tests/Console/GruffCliSummaryTest.php
  • tests/Console/ListRulesCliTest.php
  • tests/Fixtures/Cli/Golden/json-warning.json
  • tests/Fixtures/Cli/Golden/text-warning.txt
  • tests/Fixtures/DeadCode/project-wide/config/routes/block.yml
  • tests/Fixtures/DeadCode/project-wide/config/routes/inline.yaml
  • tests/Fixtures/DeadCode/project-wide/config/routes/non-fqcn.yaml
  • tests/Fixtures/DeadCode/project-wide/config/routes/quoted.yaml
  • tests/Fixtures/DeadCode/project-wide/src/Controller/RouteControllers.php
  • tests/Rule/DeadCode/ProjectDeadCodeRulesTest.php
  • tests/Rule/RuleRegressionSnapshotTest.php
  • tests/Rule/TestQuality/TestQualityRulesTest.php
✅ Files skipped from review due to trivial changes (7)
  • tests/Fixtures/DeadCode/project-wide/config/routes/non-fqcn.yaml
  • tests/Fixtures/DeadCode/project-wide/config/routes/quoted.yaml
  • tests/Console/AnalyseCliTest.php
  • tests/Fixtures/DeadCode/project-wide/config/routes/inline.yaml
  • CHANGELOG.md
  • tests/Fixtures/DeadCode/project-wide/src/Controller/RouteControllers.php
  • tests/Fixtures/Cli/Golden/json-warning.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Command/AnalysisFindingSupport.php`:
- Around line 79-80: The early return currently uses the condition "if
($projectRuleIds === [] || $filePaths === []) { return $findings; }" which
treats an empty discovered-file set as unscoped; change the logic so an empty
$filePaths does not preserve all project-rule findings. Replace the OR with an
AND (i.e. only return when both $projectRuleIds and $filePaths are empty) or
alternatively, if $filePaths is empty, ensure project-level findings are
filtered out instead of returning $findings; update the check around
$projectRuleIds, $filePaths and the return so unrelated project-wide findings
are not kept.

In `@src/Command/AnalysisPipeline.php`:
- Around line 137-143: The check that sets $hasNarrowProjectRuleContext treats
any explicit paths (e.g. analyse .) as narrowing the project and disables
streaming; update the predicate in AnalysisPipeline.php so that only genuinely
narrow paths (paths that are not the project root) set
$hasNarrowProjectRuleContext. Concretely, change the condition using
$options->paths to detect and ignore a single root path (or "." equivalent) — or
add/use a helper on the options object (e.g. isRootOnly or similar) — so
BranchReviewBuilder::shouldLoadProjectContext() still recognizes full-project
invocations and avoids an extra full-tree parse.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f125884b-c100-492c-a268-1ec4d2565468

📥 Commits

Reviewing files that changed from the base of the PR and between f6b30ae and 1db79ca.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • docs/output-formats.md
  • src/Command/AnalyseCommand.php
  • src/Command/AnalysisFindingSupport.php
  • src/Command/AnalysisPipeline.php
  • src/Command/BranchReviewBuilder.php
  • src/Diff/DiffResult.php
  • src/Rule/DeadCode/DeadCodeProjectIndex.php
  • src/Rule/RuleRegistry.php
  • tests/Console/AnalyseCliDiffTest.php
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Rule/DeadCode/DeadCodeProjectIndex.php

Comment thread src/Command/AnalysisFindingSupport.php Outdated
Comment thread src/Command/AnalysisPipeline.php Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Release v0.3.1 updates the analyzer and tooling to improve test-quality detection, changed-region/diff accounting, dead-code indexing across Symfony YAML routes, and text output ergonomics—alongside the usual version/doc/test snapshot bumps.

Changes:

  • Add advisory rule test-quality.static-analysis-redundant-test and corresponding fixtures/tests.
  • Extend dead-code project indexing to treat Symfony YAML _controller: FQCN::method callables as live references, including non-PHP source units.
  • Improve changed-region reporting (suppressedCount / diff.suppressedCount) and lead text outputs with Composite/Findings tallies; update CLI fixtures/snapshots and agent hook scripts.

Reviewed changes

Copilot reviewed 40 out of 41 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Rule/TestQuality/TestQualityRulesTest.php Refactors test-quality rule fixture assertions; adds coverage for the new static-analysis-redundant rule.
tests/Rule/RuleRegressionSnapshotTest.php Updates regression snapshot counts/hash for new rules/fixtures.
tests/Rule/RuleRegistryTest.php Registers the new rule in stable rule-id and definition snapshots.
tests/Rule/DeadCode/ProjectDeadCodeRulesTest.php Adds project-wide fixtures/tests to ensure YAML _controller references keep controllers live; parses non-PHP units.
tests/Fixtures/TestQuality/static-analysis-redundant-test.php New fixture exercising static declaration existence assertions and non-candidate cases.
tests/Fixtures/DeadCode/project-wide/src/Controller/RouteControllers.php Adds controller classes referenced (or intentionally not referenced) by YAML routes.
tests/Fixtures/DeadCode/project-wide/config/routes/quoted.yaml YAML route examples with quoted _controller values.
tests/Fixtures/DeadCode/project-wide/config/routes/non-fqcn.yaml YAML route examples that should not be treated as FQCN controller references.
tests/Fixtures/DeadCode/project-wide/config/routes/inline.yaml YAML inline mapping route example for _controller.
tests/Fixtures/DeadCode/project-wide/config/routes/block.yml YAML block mapping route example for _controller.
tests/Fixtures/Cli/Golden/text-warning.txt Updates golden text output (version + new top-of-report tallies).
tests/Fixtures/Cli/Golden/json-warning.json Updates golden JSON output version.
tests/Console/ListRulesCliTest.php Updates expected CLI version output.
tests/Console/GruffCliSummaryTest.php Updates summary output assertions to match new formatting and version.
tests/Console/AnalyseCliTest.php Updates analyse output assertions for version bump.
tests/Console/AnalyseCliDiffTest.php Adds changed-region reconciliation tests and helper methods for JSON report assertions.
src/Rule/TestQuality/StaticAnalysisRedundantTestRule.php Implements the new advisory rule for same-file *_exists-style static existence assertions.
src/Rule/RuleRegistry.php Registers the new rule; adds enabled project-rule id helper; allows project accumulators to optionally consume text units.
src/Rule/ProjectSourceTextRuleAccumulator.php Introduces marker interface for project accumulators that need non-PHP units.
src/Rule/DeadCode/UnusedInternalClassRule.php Marks unused-internal-class rule as needing text units so YAML can contribute references.
src/Rule/DeadCode/DeadCodeProjectIndex.php Adds Symfony YAML _controller parsing and reference recording for dead-code indexing.
src/Reporting/TextReporter.php Moves composite/findings tallies to the top; simplifies score/summary sections.
src/Diff/DiffResult.php Adds suppressedCount to diff metadata serialization.
src/Console/Application.php Bumps CLI version to 0.3.1.
src/Command/SummaryCommand.php Updates summary text formatting (header + Composite/Findings lines).
src/Command/BranchReviewBuilder.php Filters project-rule findings to requested file set in base snapshot comparisons.
src/Command/AnalysisPipeline.php Disables streaming when narrowed paths require project-rule context; filters project-rule findings to requested file set.
src/Command/AnalysisFindingSupport.php Adds helper to scope project-rule findings to the invocation’s requested files.
src/Command/AnalyseCommand.php Threads diff suppressedCount into DiffResult for reporting.
docs/rules.md Updates rule catalogue counts and severities; documents the new rule.
docs/output-formats.md Documents suppressedCount / diff.suppressedCount semantics for changed-region reports.
composer.lock Dev dependency version bumps for the release.
composer.json Updates package description rule count to 133.
CHANGELOG.md Adds 0.3.1 release notes.
.gruff-php.yaml Enables the new test-quality rule by default.
.goat-flow/decisions/ADR-022-test-quality-gate-parity.md Records rationale for the new advisory candidate rule.
.goat-flow/code-map.md Updates code map metadata and references the new rule.
.goat-flow/architecture.md Updates architecture doc rule counts and test-quality rule list.
.codex/hooks/gruff-code-quality.sh Enhances hook path/range extraction, severity sorting/capping/flooring, and native changed-region delegation.
.claude/settings.json Simplifies repo-root detection and makes gruff hook fail-soft when git is unavailable.
.claude/hooks/gruff-code-quality.sh Mirrors the codex hook improvements for Claude environments.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Command/BranchReviewBuilder.php Outdated
Comment on lines +310 to +314
private function sourceFilePaths(AnalysisSourceSet $sources): array
{
return array_map(
static fn(SourceFile $sourceFile): string => $sourceFile->displayPath,
$sources->discovery->files,
Comment thread src/Command/AnalysisPipeline.php Outdated
Comment on lines +334 to +338
private function sourceFilePaths(AnalysisSourceSet $sources): array
{
return array_map(
static fn(SourceFile $sourceFile): string => $sourceFile->displayPath,
$sources->discovery->files,
Comment thread .codex/hooks/gruff-code-quality.sh Outdated
Comment on lines +695 to +696
printf 'gruff-code-quality: %s %s changed-lines=%s; %s on changed lines: %s error, %s warning, %s advisory\n' \
"$binary" "$rel_path" "$ranges" "$total" "$err" "$warn" "$adv"
Comment on lines +695 to +696
printf 'gruff-code-quality: %s %s changed-lines=%s; %s on changed lines: %s error, %s warning, %s advisory\n' \
"$binary" "$rel_path" "$ranges" "$total" "$err" "$warn" "$adv"
Comment thread src/Command/BranchReviewBuilder.php

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 46 changed files in this pull request and generated no new comments.

Comment thread .goat-flow/hooks/gruff-code-quality.sh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/settings.json:
- Line 75: The root-detection logic can yield the wrong repository root in
linked worktrees: in the PreToolUse inline command (the big "gcd=...; root=...;
case" statement in .claude/settings.json) and in resolve_goat_flow_root() inside
.claude/hooks/deny-dangerous.sh you should compute root using git rev-parse
--show-toplevel first (falling back to ${CLAUDE_PROJECT_DIR:-}) instead of using
the /*) root="$(dirname "$gcd")" branch that points at git common-dir; update
the /*) branch to set root="$(git rev-parse --show-toplevel 2>/dev/null ||
true)" or simply call git rev-parse --show-toplevel before the case, and in
resolve_goat_flow_root() ensure GOAT_FLOW_ROOT is assigned (e.g., set root then
export GOAT_FLOW_ROOT) so GOAT_HOOK_LIB_DIR resolves correctly and hooks remain
discoverable and executable.

In @.codex/config.toml:
- Around line 25-43: The deny globs in .codex/config.toml are too specific
(e.g., "**/.env.local", "**/credentials") and miss variants; update the deny
rules to mirror Claude’s broader policy by replacing or adding patterns like
"**/.env*" and "**/credentials*" (or equivalent expanded globs) so all .env*
suffixes and credentials* variants are covered; modify the existing entries that
mention "**/.env", "**/.env.local", "**/.env.production", and "**/credentials"
to use the wildcarded patterns and remove redundant specific entries to keep
Codex aligned with Claude.

In @.goat-flow/hook-lib/patterns-shell.sh:
- Around line 255-258: The xargs check only matches when the segment starts with
"xargs", so pipeline-fed cases (commands where xargs appears after a pipe) are
missed; update check_destructive_segment to detect xargs in any pipeline element
by splitting CMD_NORMALIZED on pipe ('|') boundaries and running
strip_xargs_payload_command on each trimmed pipeline component (or modify
strip_xargs_payload_command to search for a word-boundary '\bxargs\b' anywhere
in the input and extract its payload), then call rm_has_recursive on the
extracted payload and block if recursive rm is present; reference functions:
check_destructive_segment, strip_xargs_payload_command, rm_has_recursive, and
variable CMD_NORMALIZED.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc5a4f9e-92f8-4750-b35f-2d2d0833cb6d

📥 Commits

Reviewing files that changed from the base of the PR and between 49a3f67 and 090c993.

⛔ Files ignored due to path filters (2)
  • composer.lock is excluded by !**/*.lock
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (64)
  • .agents/skills/goat-critique/SKILL.md
  • .agents/skills/goat-critique/references/rubric-examples.md
  • .agents/skills/goat-critique/references/sub-agent-directives.md
  • .agents/skills/goat-debug/SKILL.md
  • .agents/skills/goat-plan/SKILL.md
  • .agents/skills/goat-plan/references/issue-format.md
  • .agents/skills/goat-plan/references/milestone-examples.md
  • .agents/skills/goat-qa/SKILL.md
  • .agents/skills/goat-review/SKILL.md
  • .agents/skills/goat-review/references/automated-review.md
  • .agents/skills/goat-review/references/examples.md
  • .agents/skills/goat-review/references/refuter-spec.md
  • .agents/skills/goat-security/SKILL.md
  • .agents/skills/goat-security/references/common-threats.md
  • .agents/skills/goat-security/references/file-upload-and-paths.md
  • .agents/skills/goat-security/references/identity-and-data.md
  • .agents/skills/goat-security/references/project-policy-template.md
  • .agents/skills/goat-security/references/supply-chain-and-cicd.md
  • .agents/skills/goat/SKILL.md
  • .claude/hooks/deny-dangerous.sh
  • .claude/settings.json
  • .claude/skills/goat-critique/SKILL.md
  • .claude/skills/goat-critique/references/rubric-examples.md
  • .claude/skills/goat-critique/references/sub-agent-directives.md
  • .claude/skills/goat-debug/SKILL.md
  • .claude/skills/goat-plan/SKILL.md
  • .claude/skills/goat-plan/references/issue-format.md
  • .claude/skills/goat-plan/references/milestone-examples.md
  • .claude/skills/goat-qa/SKILL.md
  • .claude/skills/goat-review/SKILL.md
  • .claude/skills/goat-review/references/automated-review.md
  • .claude/skills/goat-review/references/examples.md
  • .claude/skills/goat-review/references/refuter-spec.md
  • .claude/skills/goat-security/SKILL.md
  • .claude/skills/goat-security/references/common-threats.md
  • .claude/skills/goat-security/references/file-upload-and-paths.md
  • .claude/skills/goat-security/references/identity-and-data.md
  • .claude/skills/goat-security/references/project-policy-template.md
  • .claude/skills/goat-security/references/supply-chain-and-cicd.md
  • .claude/skills/goat/SKILL.md
  • .codex/config.toml
  • .codex/hooks/deny-dangerous.sh
  • .goat-flow/.gitignore
  • .goat-flow/config.yaml
  • .goat-flow/hook-lib/deny-dangerous-self-test.sh
  • .goat-flow/hook-lib/patterns-shell.sh
  • .goat-flow/hook-lib/patterns-writes.sh
  • .goat-flow/lessons/workflow.md
  • .goat-flow/logs/review/README.md
  • .goat-flow/skill-playbooks/README.md
  • .goat-flow/skill-playbooks/browser-use.md
  • .goat-flow/skill-playbooks/changelog.md
  • .goat-flow/skill-playbooks/code-comments.md
  • .goat-flow/skill-playbooks/gruff-code-quality.md
  • .goat-flow/skill-playbooks/observability.md
  • .goat-flow/skill-playbooks/page-capture.md
  • .goat-flow/skill-playbooks/release-notes.md
  • .goat-flow/skill-playbooks/skill-quality-testing.md
  • .goat-flow/skill-playbooks/skill-quality-testing/adversarial-framing.md
  • .goat-flow/skill-playbooks/skill-quality-testing/deployment.md
  • .goat-flow/skill-playbooks/skill-quality-testing/tdd-iteration.md
  • .goat-flow/skill-reference/README.md
  • .goat-flow/skill-reference/skill-conventions.md
  • .goat-flow/skill-reference/skill-preamble.md
✅ Files skipped from review due to trivial changes (41)
  • .goat-flow/config.yaml
  • .claude/skills/goat-review/references/automated-review.md
  • .claude/skills/goat-review/references/examples.md
  • .claude/skills/goat-security/references/identity-and-data.md
  • .agents/skills/goat-security/references/common-threats.md
  • .goat-flow/skill-playbooks/skill-quality-testing/adversarial-framing.md
  • .goat-flow/.gitignore
  • .goat-flow/skill-reference/skill-conventions.md
  • .agents/skills/goat-plan/references/milestone-examples.md
  • .claude/skills/goat-plan/references/milestone-examples.md
  • .agents/skills/goat-security/references/identity-and-data.md
  • .claude/skills/goat-critique/references/sub-agent-directives.md
  • .goat-flow/skill-playbooks/skill-quality-testing.md
  • .claude/skills/goat-plan/references/issue-format.md
  • .goat-flow/logs/review/README.md
  • .agents/skills/goat-plan/references/issue-format.md
  • .agents/skills/goat-security/references/project-policy-template.md
  • .claude/skills/goat-critique/references/rubric-examples.md
  • .agents/skills/goat-critique/references/rubric-examples.md
  • .goat-flow/skill-playbooks/code-comments.md
  • .goat-flow/skill-reference/README.md
  • .claude/skills/goat-security/references/common-threats.md
  • .goat-flow/skill-playbooks/release-notes.md
  • .agents/skills/goat-critique/references/sub-agent-directives.md
  • .agents/skills/goat-review/references/automated-review.md
  • .claude/skills/goat-review/references/refuter-spec.md
  • .goat-flow/skill-playbooks/observability.md
  • .agents/skills/goat-security/references/file-upload-and-paths.md
  • .goat-flow/skill-playbooks/README.md
  • .agents/skills/goat-review/references/refuter-spec.md
  • .claude/skills/goat-security/references/project-policy-template.md
  • .agents/skills/goat-security/references/supply-chain-and-cicd.md
  • .goat-flow/skill-playbooks/gruff-code-quality.md
  • .agents/skills/goat-qa/SKILL.md
  • .agents/skills/goat-review/references/examples.md
  • .claude/skills/goat-security/references/file-upload-and-paths.md
  • .goat-flow/skill-playbooks/changelog.md
  • .goat-flow/skill-playbooks/browser-use.md
  • .goat-flow/lessons/workflow.md
  • .claude/skills/goat-security/references/supply-chain-and-cicd.md
  • .goat-flow/skill-playbooks/page-capture.md

Comment thread .claude/settings.json Outdated
Comment thread .codex/config.toml Outdated
Comment thread .goat-flow/hooks/deny-dangerous/patterns-shell.sh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a1477216a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +314 to +315
if ($key === '_controller' && is_string($childValue)) {
$this->recordSymfonyControllerReferenceValue($childValue, $isTestFile);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize controller keys in Symfony YAML routes

When a Symfony route YAML file uses the common top-level controller: App\Controller\FooController::index form, this walker parses the file but ignores that key because it only records _controller. In those projects, route controllers referenced from config/routes.yaml are still reported by dead-code.unused-internal-class as unused even though they are live entrypoints; treat both _controller and controller as controller callable keys.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e21dce49b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Command/HookCommand.php Outdated
Comment on lines +209 to +212
$includeRules = $this->stringListOption($input, 'include-rule');
$excludeRules = $this->stringListOption($input, 'exclude-rule');
if ($includeRules !== [] || $excludeRules !== []) {
$config = $config->withRuleSelection(new RuleSelection(rules: $includeRules, excludeRules: $excludeRules));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve config selection when adding hook excludes

When a project config already narrows selection.rules, invoking hook --exclude-rule ... replaces that selection with a new RuleSelection whose include lists are empty, and empty includes mean every enabled rule is allowed before the exclude is applied. In that scenario a focused hook run unexpectedly widens to the whole configured rule set, producing unrelated findings instead of just removing the named rule from the existing selection.

Useful? React with 👍 / 👎.

Comment thread src/Command/HookCommand.php Outdated
throw new RuntimeException(sprintf('Unable to read hook baseline: %s', $baselinePath));
}

$decoded = json_decode($contents, true, 512, JSON_THROW_ON_ERROR);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return hook JSON for malformed baselines

If --baseline points at an existing but invalid JSON file, this json_decode(..., JSON_THROW_ON_ERROR) raises JsonException, which is not caught by the surrounding DiffException | RuntimeException handler in execute(). That breaks the hook contract for a common operational error by exiting through Symfony's exception path instead of returning the in-band empty hook report with config.error.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3f178d43d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Command/HookCommand.php Outdated
Comment on lines +237 to +239
tiers: $existing->tiers,
pillars: $existing->pillars,
rules: $includeRules !== [] ? $includeRules : $existing->rules,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear inherited selection when include-rule is set

When a project config already selects tiers or pillars, preserving those fields here makes hook --include-rule X run X plus every rule matched by the configured tier/pillar, because RuleSelection::allows() treats tiers, pillars, and rules as ORed include filters. That contradicts the hook help text (“Run only the named rule id”) and can surface unrelated findings in focused editor/agent hook runs; keep the existing selection for bare excludes, but when include rules are supplied the execution selection needs to be narrowed to those rule ids.

Useful? React with 👍 / 👎.

Comment on lines +53 to +56
'file' => $finding->filePath,
'symbol' => $finding->symbol,
'qualifier' => self::qualifier($finding, $scope),
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disambiguate duplicate line findings in baselines

For line-scoped findings this identity omits the line and only includes rule/file/symbol plus a qualifier, so repeated same-rule diagnostics with the same message in one file collapse to one stableIdentity (for example two security.error-suppression @ uses have no symbol or metadata and the same message). In hook new-only mode, a baseline containing one such finding suppresses any newly added duplicate elsewhere in the same file, hiding fresh changed-code feedback from the agent.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52cbbfbd25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread package-lock.json Outdated
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@blundergoat/goat-flow/-/goat-flow-1.9.0.tgz",
"integrity": "sha512-3LVOGfdOKtyuTdAjDQ7/kjlCjOysdTq0uadKhhPnIcuEzhMYjIx2rK3H47URB9/nIU0580BfsEeVJyTlrqSPmg==",
"version": "1.9.1",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pin goat-flow to the declared 1.9.2 release

With this commit .goat-flow/config.yaml and the committed skill metadata declare goat-flow 1.9.2, and scripts/dependency-install.sh now runs npm ci, which installs exactly the version recorded here. Because the lockfile still pins @blundergoat/goat-flow to 1.9.1, a fresh setup gets an older CLI/reference package than the repo configuration and skills expect, so goat-flow audit/hook syncs can run against the wrong version; regenerate the lockfile for 1.9.2 (and consider tightening package.json) so installs match the checked-in goat-flow surface.

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1bc6814. Configure here.

Comment thread .agents/hooks.json
"hooks": [
{
"type": "command",
"command": "bash -c 'gcd=\"$(git rev-parse --git-common-dir 2>/dev/null)\"; root=\"\"; case \"$gcd\" in */.git/modules/*|.git/modules/*) root=\"$(git rev-parse --show-toplevel 2>/dev/null || true)\" ;; /*|[A-Za-z]:/*|[A-Za-z]:\\\\*) gcd=\"${gcd//\\\\//}\"; root=\"$(dirname \"$gcd\")\" ;; *) root=\"$(git rev-parse --show-toplevel 2>/dev/null || true)\" ;; esac; [ -f \"$root/.goat-flow/hooks/gruff-code-quality.sh\" ] || root=\"${CLAUDE_PROJECT_DIR:-}\"; [ -f \"$root/.goat-flow/hooks/gruff-code-quality.sh\" ] || { printf '\\''{\"decision\":\"deny\",\"reason\":\"Policy hook unavailable: git repository root unavailable.\"}\\n'\\''; exit 0; }; cd \"$root\" || { printf '\\''{\"decision\":\"deny\",\"reason\":\"Policy hook unavailable: git repository root unavailable.\"}\\n'\\''; exit 0; }; bash \"$root/.goat-flow/hooks/gruff-code-quality.sh\"'",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agents gruff hook denies when missing

Medium Severity

When .goat-flow/hooks/gruff-code-quality.sh is missing, the PostToolUse wrapper emits a JSON deny decision instead of skipping quietly. That treats an optional quality hook like a hard policy failure and can block or disrupt file edits even though the hook is designed to fail soft.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1bc6814. Configure here.

Comment thread .claude/settings.json
{
"type": "command",
"command": "gcd=\"$(git rev-parse --git-common-dir 2>/dev/null)\" || { printf 'BLOCKED: Guard cannot start: git repository root unavailable.\\n' >&2; exit 2; }; case \"$gcd\" in /*) root=\"$(dirname \"$gcd\")\" ;; *) root=\"$(git rev-parse --show-toplevel)\" ;; esac; bash \"$root/.claude/hooks/gruff-code-quality.sh\""
"command": "bash -c 'gcd=\"$(git rev-parse --git-common-dir 2>/dev/null)\"; root=\"\"; case \"$gcd\" in */.git/modules/*|.git/modules/*) root=\"$(git rev-parse --show-toplevel 2>/dev/null || true)\" ;; /*|[A-Za-z]:/*|[A-Za-z]:\\\\*) gcd=\"${gcd//\\\\//}\"; root=\"$(dirname \"$gcd\")\" ;; *) root=\"$(git rev-parse --show-toplevel 2>/dev/null || true)\" ;; esac; [ -f \"$root/.goat-flow/hooks/gruff-code-quality.sh\" ] || root=\"${CLAUDE_PROJECT_DIR:-}\"; [ -f \"$root/.goat-flow/hooks/gruff-code-quality.sh\" ] || { printf '\\''BLOCKED: Policy hook unavailable: git repository root unavailable.\\n'\\'' >&2; exit 2; }; cd \"$root\" || { printf '\\''BLOCKED: Policy hook unavailable: git repository root unavailable.\\n'\\'' >&2; exit 2; }; bash \"$root/.goat-flow/hooks/gruff-code-quality.sh\"'"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude PostToolUse blocks missing gruff

Medium Severity

Edit and Write PostToolUse hooks now exit with code 2 and a BLOCKED message when .goat-flow/hooks/gruff-code-quality.sh is unavailable. That hard-fails the hook phase for an optional analyzer instead of allowing the edit to complete without gruff output.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1bc6814. Configure here.

Comment thread .claude/settings.json
"MultiEdit(**/.pypirc)",
"MultiEdit(**/*.pfx)",
"MultiEdit(**/credentials*)",
"MultiEdit(**/.kube/config)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MultiEdit secret denies removed

Medium Severity

The diff removes all MultiEdit(**/…) permission denies for secret and credential paths while keeping parallel Read, Edit, and Write rules. If Claude still exposes MultiEdit as a separate tool, those paths may no longer be blocked at the permissions layer.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1bc6814. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1bc6814d0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/settings.json
Comment on lines 15 to 16
"Edit(**/.env*)",
"MultiEdit(**/.env*)",
"MultiEdit(**/secrets/**)",
"MultiEdit(**/*.pem)",
"MultiEdit(**/*.key)",
"MultiEdit(**/.ssh/**)",
"MultiEdit(**/.aws/**)",
"MultiEdit(**/.docker/config.json)",
"MultiEdit(**/.gnupg/**)",
"MultiEdit(**/.npmrc)",
"MultiEdit(**/.pypirc)",
"MultiEdit(**/*.pfx)",
"MultiEdit(**/credentials*)",
"MultiEdit(**/.kube/config)",
"Write(**/.env*)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore MultiEdit secret-path protections

When Claude uses MultiEdit on protected paths such as .env, secrets/**, or *.pem, this config no longer blocks it: the MultiEdit(...) deny entries were removed, the only PreToolUse hook matcher is Bash, and the PostToolUse hook now covers only Edit/Write (I checked the current .claude/settings.json for MultiEdit). That leaves a common file-edit tool able to modify secret paths without the settings deny or the deny-dangerous policy hook, even though the hook script still knows how to evaluate multiedit; add the MultiEdit deny/hook coverage back for the same protected globs.

Useful? React with 👍 / 👎.

@mattyhansen mattyhansen merged commit f036932 into main Jun 8, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants