diff --git a/assets/templates/rules/features.ai-rules.yml b/assets/templates/rules/features.ai-rules.yml index 34a8398..3c20cc6 100644 --- a/assets/templates/rules/features.ai-rules.yml +++ b/assets/templates/rules/features.ai-rules.yml @@ -6,19 +6,22 @@ file_associations: "feature.discussion.sum.md": "discussion_summary" "implementation.discussion.md": "implementation_discussion_update" "implementation.discussion.sum.md": "discussion_summary" + "README.md": "readme_skip" # Override root rule - don't update README from features/ rules: + readme_skip: + # Disable README updates when editing files in Docs/features/ + # This prevents unnecessary AI calls during feature discussions + outputs: {} + feature_request: outputs: feature_discussion: path: "Docs/features/{feature_id}/discussions/feature.discussion.md" output_type: "feature_discussion_writer" implementation_gate: - enabled: false # Disabled by default - enable when feature is ready for implementation path: "Docs/features/{feature_id}/discussions/implementation.discussion.md" output_type: "implementation_gate_writer" - # To enable: set "enabled: true" in your project's .ai-rules.yml - # This prevents unnecessary AI calls during initial feature discussion phase feature_discussion_update: outputs: diff --git a/assets/templates/rules/root.ai-rules.yml b/assets/templates/rules/root.ai-rules.yml index 72fe236..efbcb61 100644 --- a/assets/templates/rules/root.ai-rules.yml +++ b/assets/templates/rules/root.ai-rules.yml @@ -9,7 +9,6 @@ rules: readme: outputs: normalize: - enabled: false # Disabled by default - enable in your project when you want AI to maintain README path: "{repo}/README.md" output_type: "readme_normalizer" instruction: | diff --git a/automation/patcher.py b/automation/patcher.py index 5c0ef62..85dddb2 100644 --- a/automation/patcher.py +++ b/automation/patcher.py @@ -121,8 +121,13 @@ def generate_output( save_debug_artifacts(repo_root, output_rel, raw_path, clean_path, sanitized_path, final_patch_path) # Check if the final patch is empty after sanitization. + # This is normal when AI determines no changes are needed (e.g., gated outputs) if not final_patch_path.read_text(encoding="utf-8").strip(): - raise PatchGenerationError("AI returned empty patch") + # Empty patch is not an error - AI decided not to make changes + # Common cases: + # - implementation_gate_writer when status != READY_FOR_IMPLEMENTATION + # - AI determines output is already correct + return # Skip silently # Apply the generated patch to the Git repository. apply_patch(repo_root, final_patch_path, patch_level, output_rel) @@ -414,10 +419,8 @@ def sanitize_unified_patch(patch: str) -> str: patch: The raw unified diff string. Returns: - The sanitized diff string. - - Raises: - PatchGenerationError: If the sanitized patch is missing a 'diff --git' header. + The sanitized diff string, or empty string if no diff header found. + An empty result indicates the AI chose not to make changes (e.g., gated output). """ lines = patch.replace("\r", "").splitlines() cleaned = [] @@ -427,11 +430,13 @@ def sanitize_unified_patch(patch: str) -> str: continue cleaned.append(line) text = "\n".join(cleaned) - - # Ensure the patch still contains a 'diff --git' header after sanitization. + + # Look for the 'diff --git' header diff_start = text.find("diff --git") if diff_start == -1: - raise PatchGenerationError("Sanitized patch missing diff header") + # No diff header means AI chose not to generate a patch + # This is normal for gated outputs or when AI determines no changes needed + return "" return text[diff_start:] + "\n"