Skip to content

feat(rules): implement auto-fixes for CC-MEM-005 and CC-MEM-007 - #152

Merged
avifenesh merged 5 commits into
mainfrom
feature/auto-fixes-memory-rules-16
Feb 4, 2026
Merged

feat(rules): implement auto-fixes for CC-MEM-005 and CC-MEM-007#152
avifenesh merged 5 commits into
mainfrom
feature/auto-fixes-memory-rules-16

Conversation

@avifenesh

Copy link
Copy Markdown
Collaborator

Summary

Implements auto-fix capabilities for two CLAUDE.md validation rules:

  • CC-MEM-005: Delete lines containing generic instructions ("be helpful", "be accurate", etc.)
  • CC-MEM-007: Replace weak constraint language with stronger alternatives

Closes #16

Changes

Auto-fix for CC-MEM-005 (Generic Instructions)

  • Detects and removes entire lines containing generic phrases Claude already knows
  • Uses Fix::delete() with safe=true (high certainty)
  • Patterns: "be helpful", "be accurate", "think step by step", "be concise", "be professional", "be thorough"

Auto-fix for CC-MEM-007 (Weak Constraints)

  • Replaces weak language with strong alternatives in critical sections
  • Replacement mappings:
    • "should" → "must" (safe)
    • "try to" → "must" (safe)
    • "consider" → "ensure" (safe)
    • "maybe" → "" delete (safe)
    • "could" → "must" (unsafe - context dependent)
    • "possibly" → "" delete (unsafe)

CRLF Support

  • Fixed byte offset calculation to handle both LF (Unix) and CRLF (Windows) line endings
  • Ensures correct fix positions regardless of file origin

Test Plan

  • 721 tests passing (18 new tests for this feature)
  • Tests cover byte offset accuracy for both LF and CRLF
  • Tests verify fix application produces expected output
  • Edge cases: last line without newline, multiple fixes in one file
  • cargo clippy clean
  • cargo fmt --check clean

…kConstraint

Add start_byte and end_byte fields to GenericInstruction and WeakConstraint
structs to enable precise auto-fix positioning. Update find_generic_instructions()
and find_weak_constraints() to track cumulative byte offsets while iterating
through lines.

This enables the Fix::delete() and Fix::replace() operations to work with
exact byte positions for CC-MEM-005 and CC-MEM-007 rules.
Add Fix::delete() to CC-MEM-005 (generic instructions) to remove entire lines
containing generic instructions like "be helpful", "be accurate", etc.

Add Fix::replace() to CC-MEM-007 (weak constraints) with intelligent mapping:
- "should" -> "must" (safe)
- "try to" -> "must" (safe)
- "consider" -> "ensure" (safe)
- "maybe" -> "" (delete, safe)
- "might want to" -> "must" (safe)
- "could" -> "must" (unsafe - could have other meanings)
- "possibly" -> "" (delete, unsafe)

Closes #16
The byte offset calculation in find_generic_instructions() and
find_weak_constraints() assumed all line endings are single-byte LF,
but Windows files use CRLF which is 2 bytes. This caused incorrect
byte offsets on Windows, breaking auto-fix functionality.

Fix detects the line ending type at the start of each function and
uses the appropriate byte count (2 for CRLF, 1 for LF).

Added 6 tests for CRLF content handling.
Copilot AI review requested due to automatic review settings February 4, 2026 08:54
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @avifenesh, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the utility of the CLAUDE.md validation rules by introducing automated fixing capabilities for generic instructions and weak constraint language. These changes streamline the process of enforcing best practices in Claude prompts and ensure consistent application of fixes across different operating systems by robustly handling various line ending formats.

Highlights

  • Auto-fix for CC-MEM-005 (Generic Instructions): Implemented automatic deletion of lines containing generic instructions (e.g., 'be helpful', 'be accurate') in CLAUDE.md files. This fix is marked as safe.
  • Auto-fix for CC-MEM-007 (Weak Constraints): Added auto-fix functionality to replace weak constraint language (e.g., 'should', 'try to', 'consider') with stronger alternatives ('must', 'ensure') or delete them ('maybe', 'possibly'). The safety of these replacements is context-dependent and explicitly marked.
  • CRLF Line Ending Support: Enhanced byte offset calculations to correctly handle both LF (Unix) and CRLF (Windows) line endings, ensuring accurate application of fixes regardless of the file's origin.
Changelog
  • CHANGELOG.md
    • Added entry for auto-fix support for CC-MEM-005 and CC-MEM-007 memory rules.
    • Included mention of CRLF line ending support for correct byte offsets on Windows.
  • crates/agnix-core/src/rules/claude_md.rs
    • Imported Fix struct from diagnostics module.
    • Integrated Fix::delete() for CC-MEM-005 diagnostics, targeting entire lines with generic instructions.
    • Implemented get_weak_constraint_replacement function to determine appropriate replacements and safety for CC-MEM-007.
    • Added Fix::replace() for CC-MEM-007 diagnostics, applying replacements based on the identified weak language.
    • Introduced new test cases for CC-MEM-005 and CC-MEM-007 auto-fixes, covering byte position accuracy, fix application, and various scenarios.
  • crates/agnix-core/src/schemas/claude_md.rs
    • Extended GenericInstruction and WeakConstraint structs with start_byte and end_byte fields to store precise byte offsets for fixes.
    • Modified find_generic_instructions and find_weak_constraints functions to calculate and populate these byte offsets, including logic to detect and account for CRLF line endings.
    • Added comprehensive test cases to verify byte offset calculations for both generic instructions and weak constraints, including single-line, multi-line, last-line-no-newline, and CRLF scenarios.
  • knowledge-base/VALIDATION-RULES.md
    • Updated the table to reflect auto-fix capability for CC-MEM-007.
    • Adjusted the 'Claude Memory' rule count to reflect the added auto-fix.
Activity
  • The pull request introduces auto-fix capabilities for CLAUDE.md validation rules CC-MEM-005 and CC-MEM-007.
  • It resolves issue Implement auto-fixes for memory rules (CC-MEM-005, CC-MEM-007) #16, which likely requested these auto-fix features.
  • 18 new tests have been added to thoroughly validate the new auto-fix logic, covering byte offset accuracy for both LF and CRLF, correct fix application, and various edge cases.
  • The project maintains a clean state with cargo clippy and cargo fmt --check.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@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: 245776b715

ℹ️ 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 +38 to +39
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 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 Handle mixed line endings when computing byte offsets

The new byte-offset logic assumes a single line-ending style for the entire file (line_ending_len is 2 if any \r\n exists). If a CLAUDE.md mixes LF and CRLF (which can happen when concatenating snippets from different sources), every LF-only line after the first CRLF will have offsets overcounted by 1 byte, so the auto-fix ranges can delete or replace the wrong characters. Consider computing the line-ending length per line (e.g., using split_inclusive('\n') or tracking the original slice) so offsets match the actual bytes on each line.

Useful? React with 👍 / 👎.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request successfully implements auto-fix capabilities for CC-MEM-005 (generic instructions) and CC-MEM-007 (weak constraints), including robust CRLF line ending support. The changes are well-tested with comprehensive unit tests for byte offset accuracy and fix application. The CHANGELOG.md and VALIDATION-RULES.md have been updated accordingly. One area for improvement is ensuring all occurrences of weak constraint language on a single line are detected and fixed.

@avifenesh

Copy link
Copy Markdown
Collaborator Author

Thanks for the review feedback! The mixed line endings edge case is noted - it's a rare scenario for CLAUDE.md files (which are typically created and edited by a single tool), but I'll track it as a follow-up improvement. The current implementation correctly handles the common cases of fully LF or fully CRLF files.

@avifenesh
avifenesh merged commit bdf49ce into main Feb 4, 2026
19 checks passed
@avifenesh
avifenesh deleted the feature/auto-fixes-memory-rules-16 branch February 4, 2026 09:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request implements auto-fix capabilities for two CLAUDE.md validation rules (CC-MEM-005 and CC-MEM-007), enabling automatic correction of common memory file issues. The implementation adds byte offset tracking to support precise text replacement and includes comprehensive support for both Unix (LF) and Windows (CRLF) line endings.

Changes:

  • Added auto-fix for CC-MEM-005 to delete entire lines containing generic instructions Claude already knows
  • Added auto-fix for CC-MEM-007 to replace weak constraint language ("should", "try to", "consider", etc.) with stronger alternatives ("must", "ensure") in critical sections
  • Enhanced byte offset calculation to handle both LF and CRLF line endings for accurate fix positioning

Reviewed changes

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

File Description
knowledge-base/VALIDATION-RULES.md Updated documentation to reflect CC-MEM-007 is now auto-fixable and incremented Claude Memory auto-fix count to 3
crates/agnix-core/src/schemas/claude_md.rs Added byte offset tracking (start_byte, end_byte) to GenericInstruction and WeakConstraint structs; implemented line ending detection for accurate byte position calculation; added 18 comprehensive tests for byte offsets and fix application with both LF and CRLF line endings
crates/agnix-core/src/rules/claude_md.rs Integrated Fix objects into CC-MEM-005 and CC-MEM-007 diagnostics; implemented get_weak_constraint_replacement() function with safety flags for different replacement strategies; added tests verifying fix generation and application
CHANGELOG.md Documented the new auto-fix features for CC-MEM-005 and CC-MEM-007

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

Comment on lines +253 to +264
fn get_weak_constraint_replacement(text: &str) -> (Option<&'static str>, bool) {
match text.to_lowercase().as_str() {
"should" => (Some("must"), true),
"try to" => (Some("must"), true),
"consider" => (Some("ensure"), true),
"maybe" => (Some(""), true),
"might want to" => (Some("must"), true),
"could" => (Some("must"), false),
"possibly" => (Some(""), false),
_ => (None, false),
}
}

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The replacement function expects exact single-space patterns (e.g., "try to", "might want to"), but the regex pattern that captures these uses \s+ which matches one or more whitespace characters. If the source text has "try to" (multiple spaces) or "might\twant\tto" (tabs), the regex will match it but this function will return (None, false), resulting in a diagnostic without a fix.

Consider normalizing the input text by collapsing consecutive whitespace to a single space before matching, to ensure fixes are provided for these cases as well.

Copilot uses AI. Check for mistakes.
Comment on lines 225 to 234
if let Some(mat) = weak_pattern.find(line) {
results.push(WeakConstraint {
line: line_num + 1,
column: mat.start(),
text: mat.as_str().to_string(),
section: section_name.clone(),
start_byte: line_start + mat.start(),
end_byte: line_start + mat.end(),
});
}

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The code only finds the first weak constraint per line using weak_pattern.find(line). If a line contains multiple weak words (e.g., "You should consider doing this"), only "should" will be detected and the other weak words will be missed.

Consider using weak_pattern.find_iter(line) to find all occurrences on each line, ensuring all weak constraints are flagged and can be fixed.

Copilot uses AI. Check for mistakes.
Comment on lines +872 to +873
// Each weak word generates a separate diagnostic with its own fix
assert!(mem007.len() >= 1);

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The comment states "Each weak word generates a separate diagnostic with its own fix" but this is not accurate. The find_weak_constraints function only finds the first weak word per line (using find() instead of find_iter()), so only one diagnostic per line is generated, not one per weak word. This test assertion mem007.len() >= 1 will pass with just one diagnostic, which may mask the issue.

Consider updating the comment to reflect the actual behavior, or fix the implementation to find all weak words per line as the comment suggests.

Suggested change
// Each weak word generates a separate diagnostic with its own fix
assert!(mem007.len() >= 1);
// Currently, only the first weak word on a line generates a diagnostic with its own fix
assert_eq!(mem007.len(), 1);

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +39
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The call to content.contains("\r\n") scans the entire file content to detect line ending type. For large files, this could be inefficient. Consider detecting the line ending type by checking only the first few lines, or detecting it on-the-fly as lines are processed.

However, since CLAUDE.md files are typically small, this is likely not a significant performance concern in practice.

Suggested change
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };
// Detect line ending type: CRLF (2 bytes) or LF (1 byte), based on first newline
let line_ending_len = match content.find('\n') {
Some(pos) if pos > 0 && content.as_bytes()[pos - 1] == b'\r' => 2,
Some(_) => 1,
None => 1,
};

Copilot uses AI. Check for mistakes.
Comment on lines +205 to +206
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The call to content.contains("\r\n") scans the entire file content to detect line ending type. For large files, this could be inefficient. Consider detecting the line ending type by checking only the first few lines, or detecting it on-the-fly as lines are processed.

However, since CLAUDE.md files are typically small, this is likely not a significant performance concern in practice.

Suggested change
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };
// Detect line ending type based on the first newline: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = match content.find('\n') {
Some(pos) if pos > 0 && content.as_bytes()[pos - 1] == b'\r' => 2,
Some(_) => 1,
None => 1,
};

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +49
let content_len = content.len();

// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };

for (line_num, line) in content.lines().enumerate() {
let line_start = byte_offset;
// Calculate the end of line: line length + line ending bytes (if not at end of file)
let line_end = if byte_offset + line.len() < content_len {
byte_offset + line.len() + line_ending_len // Include line ending
} else {
byte_offset + line.len() // Last line may not have trailing newline
};

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The line ending detection at line 39 assumes all lines in the file have the same type of line ending (either all LF or all CRLF). This could produce incorrect byte offsets if the file has mixed line endings.

Consider detecting the line ending type for each line individually, or document this limitation. For most real-world scenarios this should be fine since files typically have consistent line endings, but mixed line endings could cause incorrect fix positions.

Suggested change
let content_len = content.len();
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };
for (line_num, line) in content.lines().enumerate() {
let line_start = byte_offset;
// Calculate the end of line: line length + line ending bytes (if not at end of file)
let line_end = if byte_offset + line.len() < content_len {
byte_offset + line.len() + line_ending_len // Include line ending
} else {
byte_offset + line.len() // Last line may not have trailing newline
};
let content_bytes = content.as_bytes();
let content_len = content_bytes.len();
for (line_num, line) in content.lines().enumerate() {
let line_start = byte_offset;
let base_end = line_start + line.len();
// Determine the actual line ending length (if any) for this line
let newline_len = if base_end < content_len {
match content_bytes[base_end] {
b'\r' if base_end + 1 < content_len && content_bytes[base_end + 1] == b'\n' => 2,
b'\n' => 1,
_ => 0,
}
} else {
0
};
// Calculate the end of line including its specific line ending, if present
let line_end = base_end + newline_len;

Copilot uses AI. Check for mistakes.
Comment on lines +205 to +206
// Detect line ending type: CRLF (2 bytes) or LF (1 byte)
let line_ending_len = if content.contains("\r\n") { 2 } else { 1 };

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The line ending detection at line 206 assumes all lines in the file have the same type of line ending (either all LF or all CRLF). This could produce incorrect byte offsets if the file has mixed line endings.

Consider detecting the line ending type for each line individually, or document this limitation. For most real-world scenarios this should be fine since files typically have consistent line endings, but mixed line endings could cause incorrect fix positions.

Copilot uses AI. Check for mistakes.
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.

Implement auto-fixes for memory rules (CC-MEM-005, CC-MEM-007)

2 participants