doc-ears-fixer
Automated fix skill that reads review reports and applies fixes to EARS documents - handles broken links, element IDs, missing files, and iterative improvement
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
doc-ears-fixer
Purpose
Automated fix skill that reads the latest audit/review report and applies fixes to EARS (Easy Approach to Requirements Syntax) documents. This skill bridges the gap between doc-ears-reviewer/doc-ears-audit (which identify issues) and the corrected EARS, enabling iterative improvement cycles.
Layer: 3 (EARS Quality Improvement)
Upstream: EARS document, Audit/Review Report (EARS-NN.A_audit_report_vNNN.md preferred, EARS-NN.R_review_report_vNNN.md legacy), PRD (source requirements)
Downstream: Fixed EARS, Fix Report (EARS-NN.F_fix_report_vNNN.md)
When to Use This Skill
Use doc-ears-fixer when:
-
After Review: Run after
doc-ears-revieweridentifies issues - Iterative Improvement: Part of Review -> Fix -> Review cycle
- Automated Pipeline: CI/CD integration for quality gates
- Batch Fixes: Apply fixes to multiple EARS based on review reports
Do NOT use when:
- No audit/review report exists (run
doc-ears-auditordoc-ears-reviewerfirst) - Creating new EARS (use
doc-earsordoc-ears-autopilot) - Only need validation (use
doc-ears-validator)
Skill Dependencies
| Skill | Purpose | When Used |
|---|---|---|
doc-ears-audit |
Preferred source of normalized findings | Input (reads audit report) |
doc-ears-reviewer |
Legacy/alternate source of issues to fix | Input (reads review report) |
doc-naming |
Element ID standards | Fix element IDs |
doc-ears |
EARS creation rules | Create missing sections |
doc-prd-reviewer |
Upstream PRD validation | Check upstream alignment |
Workflow Overview
flowchart TD
A[Input: EARS Path] --> B[Find Latest Audit/Review Report]
B --> C{Report Found?}
C -->|No| D[Run doc-ears-audit or doc-ears-reviewer First]
C -->|Yes| E[Parse Report]
E --> F[Categorize Issues]
subgraph FixPhases["Fix Phases"]
F --> F0[Phase 0: Fix Structure Violations]
F0 --> G[Phase 1: Create Missing Files]
G --> H[Phase 2: Fix Broken Links]
H --> I[Phase 3: Fix Element IDs]
I --> J[Phase 4: Fix Content Issues]
J --> K[Phase 5: Update References]
K --> K2[Phase 6: Handle Upstream Drift]
end
K2 --> L[Write Fixed EARS]
L --> M[Generate Fix Report]
M --> N{Re-run Review?}
N -->|Yes| O[Invoke doc-ears-reviewer]
O --> P{Score >= Threshold?}
P -->|No, iterations < max| F
P -->|Yes| Q[COMPLETE]
N -->|No| Q
Fix Phases
Phase 0: Fix Structure Violations (CRITICAL)
Fixes EARS documents that are not in nested folders. This phase runs FIRST because all subsequent phases depend on correct folder structure.
Nested Folder Rule: ALL EARS MUST be in nested folders regardless of document size.
Required Structure:
| EARS Type | Required Location |
|---|---|
| Monolithic | docs/03_EARS/EARS-NN_{slug}/EARS-NN_{slug}.md |
Fix Actions:
| Issue Code | Issue | Fix Action |
|---|---|---|
| REV-STR001 | EARS not in nested folder | Create folder, move file, update all links |
| REV-STR002 | EARS folder name doesn't match EARS ID | Rename folder to match |
| REV-STR003 | Monolithic EARS >25KB should be sectioned | Flag for manual review |
Structure Fix Workflow:
def fix_ears_structure(ears_path: str) -> list[Fix]:
"""Fix EARS structure violations."""
fixes = []
filename = os.path.basename(ears_path)
parent_folder = os.path.dirname(ears_path)
# Extract EARS ID and slug from filename
match = re.match(r'EARS-(\d+)_([^/]+)\.md', filename)
if not match:
return [] # Cannot auto-fix invalid filename
ears_id = match.group(1)
slug = match.group(2)
expected_folder = f"EARS-{ears_id}_{slug}"
# Check if already in nested folder
if os.path.basename(parent_folder) != expected_folder:
# Create nested folder
new_folder = os.path.join(os.path.dirname(parent_folder), expected_folder)
os.makedirs(new_folder, exist_ok=True)
# Move file
new_path = os.path.join(new_folder, filename)
shutil.move(ears_path, new_path)
fixes.append(f"Moved {ears_path} to {new_path}")
# Update upstream links in moved file
content = Path(new_path).read_text()
updated_content = content.replace('../02_PRD/', '../../02_PRD/')
updated_content = updated_content.replace('../01_BRD/', '../../01_BRD/')
Path(new_path).write_text(updated_content)
fixes.append(f"Updated relative links for nested folder structure")
return fixes
Link Path Updates After Move:
| Original Path | Updated Path |
|---|---|
../02_PRD/PRD-01_slug/PRD-01.md |
../../02_PRD/PRD-01_slug/PRD-01.md |
../01_BRD/BRD-01_slug/BRD-01.md |
../../01_BRD/BRD-01_slug/BRD-01.md |
Phase 1: Create Missing Files
Creates files that are referenced but don't exist.
Scope:
| Missing File | Action | Template Used |
|---|---|---|
EARS-00_GLOSSARY.md |
Create EARS glossary | Glossary template |
EARS-NN_APPENDIX_*.md |
Create appendix placeholder | Appendix template |
| Pattern library files | Create placeholder with TODO sections | Pattern template |
| Constraint files | Create placeholder | Constraint template |
EARS Glossary Template:
---
title: "EARS-00: Requirements Glossary"
tags:
- ears
- glossary
- reference
custom_fields:
document_type: glossary
artifact_type: EARS-REFERENCE
layer: 3
---
# EARS-00: Requirements Glossary
Common terminology used across all EARS Requirements Documents.
## EARS Pattern Terms
| Term | Definition | Context |
|------|------------|---------|
| Ubiquitous | Always active requirement | Pattern type |
| Event-Driven | Triggered by specific event | Pattern type |
| Unwanted Behavior | Exception handling requirement | Pattern type |
| State-Driven | Active in specific state | Pattern type |
| Optional Feature | Conditionally included requirement | Pattern type |
| Complex | Multi-condition requirement | Pattern type |
## Requirement Keywords
| Term | Definition | Context |
|------|------------|---------|
| SHALL | Mandatory requirement | Normative |
| SHOULD | Recommended requirement | Advisory |
| MAY | Optional requirement | Permissive |
| MUST | Absolute requirement | Critical |
## Domain Terms
<!-- Add project-specific terminology below -->
| Term | Definition | Context |
|------|------------|---------|
| [Term] | [Definition] | [Where used] |
Pattern Placeholder Template:
---
title: "EARS Pattern: [Pattern Name]"
tags:
- ears
- pattern-spec
- reference
custom_fields:
document_type: pattern-spec
status: placeholder
created_by: doc-ears-fixer
---
# EARS Pattern: [Pattern Name]
> **Status**: Placeholder - Requires completion
## 1. Pattern Overview
[TODO: Document pattern overview]
## 2. Pattern Template
```text
[TODO: Define EARS pattern template]
3. Pattern Examples
| Example ID | Requirement | Pattern Type |
|---|---|---|
| EX-XX-01 | [Example requirement] | [Type] |
4. Constraints
[TODO: Document pattern constraints]
Created by doc-ears-fixer as placeholder. Complete this document to resolve broken link issues.
---
### Phase 2: Fix Broken Links
Updates links to point to correct locations.
**Fix Actions**:
| Issue Code | Issue | Fix Action |
|------------|-------|------------|
| REV-L001 | Broken internal link | Update path or create target file |
| REV-L002 | External link unreachable | Add warning comment, keep link |
| REV-L003 | Absolute path used | Convert to relative path |
| REV-L004 | Broken PRD reference | Update to correct PRD path |
**Path Resolution Logic**:
```python
def fix_link_path(ears_location: str, target_path: str) -> str:
"""Calculate correct relative path based on EARS location."""
# Monolithic EARS: docs/03_EARS/EARS-01.md
# Sectioned EARS: docs/03_EARS/EARS-01_slug/EARS-01.3_section.md
if is_sectioned_ears(ears_location):
# Need to go up one more level
return "../" + calculate_relative_path(ears_location, target_path)
else:
return calculate_relative_path(ears_location, target_path)
PRD Link Fix:
| EARS Type | Original Link | Fixed Link |
|---|---|---|
| Monolithic | ../02_PRD/PRD-01.md |
../02_PRD/PRD-01.md |
| Sectioned | ../02_PRD/PRD-01.md |
../../02_PRD/PRD-01.md |
Phase 3: Fix Element IDs
Converts invalid element IDs to correct format.
Conversion Rules:
| Pattern | Issue | Conversion |
|---|---|---|
EARS.NN.01.SS |
Code 01 invalid for EARS | EARS.NN.25.SS (EARS Statement) |
EARS.NN.22.SS |
Code 22 invalid for EARS | EARS.NN.26.SS (Constraint Statement) |
REQ-XXX |
Legacy pattern | EARS.NN.25.SS |
CON-XXX |
Legacy pattern | EARS.NN.26.SS |
PAT-XXX |
Legacy pattern | EARS.NN.25.SS |
Type Code Mapping (EARS-specific valid codes: 25, 26):
| Invalid Code | Valid Code | Element Type |
|---|---|---|
| 01 | 25 | EARS Statement |
| 02 | 25 | EARS Statement |
| 03 | 26 | Constraint Statement |
| 05 | 25 | EARS Statement |
| 06 | 26 | Constraint Statement |
| 22 | 25 | EARS Statement |
| 33 | 26 | Constraint Statement |
Regex Patterns:
# Find element IDs with invalid type codes for EARS
invalid_ears_type_01 = r'EARS\.(\d{2})\.01\.(\d{2})'
replacement_01 = r'EARS.\1.25.\2'
invalid_ears_type_22 = r'EARS\.(\d{2})\.22\.(\d{2})'
replacement_22 = r'EARS.\1.26.\2'
# Find legacy patterns
legacy_req = r'###\s+REQ-(\d+):'
legacy_con = r'###\s+CON-(\d+):'
legacy_pat = r'###\s+PAT-(\d+):'
Phase 4: Fix Content Issues
Addresses placeholders, incomplete content, and EARS-specific syntax issues.
Fix Actions:
| Issue Code | Issue | Fix Action |
|---|---|---|
| REV-P001 | [TODO] placeholder |
Flag for manual completion (cannot auto-fix) |
| REV-P002 | [TBD] placeholder |
Flag for manual completion (cannot auto-fix) |
| REV-P003 | Template date YYYY-MM-DD |
Replace with current date |
| REV-P004 | Template name [Name] |
Replace with metadata author or flag |
| REV-P005 | Empty section | Add minimum template content |
| REV-E001 | Missing EARS keyword (shall/should/may) | Flag for manual review |
| REV-E002 | Invalid EARS pattern structure | Flag for manual review |
| REV-E003 | Missing trigger condition | Flag for manual review |
Auto-Replacements:
replacements = {
'YYYY-MM-DDTHH:MM:SS': datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
'YYYY-MM-DD': datetime.now().strftime('%Y-%m-%d'),
'MM/DD/YYYY': datetime.now().strftime('%m/%d/%Y'),
'[Current date]': datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
'[System Name]': extract_system_name_from_metadata(),
}
EARS Pattern Validation:
| Pattern Type | Required Structure | Auto-Fix |
|---|---|---|
| Ubiquitous | "The [system] shall [action]" | No (flag) |
| Event-Driven | "When [trigger], the [system] shall [action]" | No (flag) |
| State-Driven | "While [state], the [system] shall [action]" | No (flag) |
| Unwanted | "If [condition], then the [system] shall [action]" | No (flag) |
| Optional | "Where [feature], the [system] shall [action]" | No (flag) |
Phase 5: Update References
Ensures traceability and cross-references are correct.
Fix Actions:
| Issue | Fix Action |
|---|---|
Missing @ref: for created files |
Add reference tag |
| Incorrect cross-EARS path | Update to correct relative path |
| Missing PRD traceability | Add PRD reference with @trace: PRD-NN |
| Missing traceability entry | Add to traceability matrix |
Traceability Format:
<!-- Traceability to PRD -->
@trace: PRD-01.22.01 -> EARS-01.25.01
<!-- Reference to upstream -->
@ref: PRD-01 Section 3 (../02_PRD/PRD-01.md#3-feature-requirements)
Phase 6: Handle Upstream Drift (Auto-Merge)
Addresses issues where upstream PRD documents have changed since EARS creation. This phase implements a tiered auto-merge system based on drift percentage.
6.0.1 Hash Validation Fixes
FIX-H001: Invalid Hash Placeholder
Trigger: Hash contains placeholder instead of SHA-256
Fix:
sha256sum <upstream_file_path> | cut -d' ' -f1
Update cache with: sha256:<64_hex_output>
FIX-H002: Missing Hash Prefix
Trigger: 64 hex chars but missing sha256: prefix
Fix: Prepend sha256: to value
FIX-H003: Upstream File Not Found
Trigger: Cannot compute hash (file missing)
Fix: Set drift_detected: true, add to manual review
| Code | Description | Auto-Fix | Severity |
|---|---|---|---|
| FIX-H001 | Replace placeholder hash with actual SHA-256 | Yes | Error |
| FIX-H002 | Add missing sha256: prefix | Yes | Warning |
| FIX-H003 | Upstream file not found | Partial | Error |
Upstream: PRD (Layer 2)
Downstream: BDD (Layer 4)
ID Pattern: EARS.NN.TT.SS (e.g., EARS.01.25.13)
Drift Issue Codes (from doc-ears-reviewer Check #9)
| Code | Severity | Description | Auto-Fix Tier |
|---|---|---|---|
| REV-D001 | Warning | PRD modified after EARS | Tier 1-2 |
| REV-D002 | Warning | Referenced PRD section content changed | Tier 1-3 |
| REV-D003 | Info | PRD version incremented | Tier 1 |
| REV-D004 | Info | New features added to PRD | Tier 1-2 |
| REV-D005 | Error | Critical PRD modification (>20% change) | Tier 3 |
Tiered Auto-Merge System
Tier Classification:
| Tier | Change % | Auto-Merge | Version Action | Description |
|---|---|---|---|---|
| Tier 1 | < 5% | Full auto-merge | Patch (+0.0.1) | Additions, threshold updates |
| Tier 2 | 5-15% | Auto-merge with changelog | Minor (+0.1.0) | Significant additions, minor modifications |
| Tier 3 | > 15% | Archive + regenerate | Major (+1.0.0) | Structural changes, major content drift |
Change Percentage Calculation
def calculate_drift_percentage(
upstream_current: str,
upstream_cached: str,
referenced_sections: list[str]
) -> float:
"""
Calculate drift percentage between cached and current upstream.
Args:
upstream_current: Current PRD content
upstream_cached: Cached PRD content from last sync
referenced_sections: List of PRD sections referenced by EARS
Returns:
Drift percentage (0.0 - 100.0)
"""
# Extract referenced sections only
current_sections = extract_sections(upstream_current, referenced_sections)
cached_sections = extract_sections(upstream_cached, referenced_sections)
# Calculate line-based diff
diff = unified_diff(cached_sections.splitlines(), current_sections.splitlines())
added_lines = sum(1 for line in diff if line.startswith('+') and not line.startswith('+++'))
removed_lines = sum(1 for line in diff if line.startswith('-') and not line.startswith('---'))
total_lines = max(len(cached_sections.splitlines()), 1)
# Weighted calculation: additions weighted 1.0, removals weighted 1.5
drift_score = (added_lines * 1.0 + removed_lines * 1.5) / total_lines * 100
return min(drift_score, 100.0)
def determine_tier(drift_percentage: float) -> int:
"""Determine merge tier based on drift percentage."""
if drift_percentage < 5.0:
return 1
elif drift_percentage <= 15.0:
return 2
else:
return 3
Tier 1: Auto-Merge (< 5% Drift)
Trigger Conditions:
- Minor additions to PRD sections
- Threshold/limit updates
- Clarification text additions
- No structural changes
Auto-Merge Actions:
| Change Type | Action | ID Assignment |
|---|---|---|
| New requirement in PRD | Add EARS statement | Next sequential ID (e.g., EARS.01.25.13) |
| Updated threshold | Update constraint value | Keep existing ID |
| Added clarification | Append to existing statement | Keep existing ID |
ID Generation for New Requirements:
def generate_next_ears_id(ears_doc: str, type_code: str = "25") -> str:
"""
Generate next sequential EARS ID.
ID Pattern: EARS.NN.TT.SS
- NN: Document number (01-99)
- TT: Type code (25=Statement, 26=Constraint)
- SS: Sequence number (01-99)
Args:
ears_doc: Path to EARS document
type_code: Type code ("25" for statement, "26" for constraint)
Returns:
Next available EARS ID
"""
# Extract document number from filename
doc_num = extract_doc_number(ears_doc) # e.g., "01" from "EARS-01.md"
# Find highest existing sequence for this type
pattern = rf'EARS\.{doc_num}\.{type_code}\.(\d{{2}})'
existing_ids = re.findall(pattern, read_file(ears_doc))
if existing_ids:
max_seq = max(int(seq) for seq in existing_ids)
next_seq = str(max_seq + 1).zfill(2)
else:
next_seq = "01"
return f"EARS.{doc_num}.{type_code}.{next_seq}"
Version Update:
# Tier 1: Increment patch version
# 1.0.0 -> 1.0.1
current_version = "1.0.0"
new_version = increment_version(current_version, "patch") # "1.0.1"
Tier 2: Auto-Merge with Changelog (5-15% Drift)
Trigger Conditions:
- Multiple new features in PRD
- Section reorganization (minor)
- Requirement refinements
- New constraint categories
Auto-Merge Actions:
| Change Type | Action | Documentation |
|---|---|---|
| New feature section | Generate EARS statements | Changelog entry |
| Modified requirements | Update existing statements | Changelog + diff |
| New constraints | Add constraint statements | Changelog entry |
Changelog Entry Format:
## Changelog Entry (Auto-Merge Tier 2)
**Date**: 2026-02-10T16:00:00
**Source**: PRD-01.md (v1.2.0 -> v1.3.0)
**Drift Percentage**: 8.3%
### Additions
| New ID | Statement Summary | Source PRD Section |
|--------|-------------------|-------------------|
| EARS.01.25.13 | System shall support batch processing | PRD-01 Section 5.2 |
| EARS.01.25.14 | System shall provide progress notifications | PRD-01 Section 5.3 |
| EARS.01.26.05 | Response time constraint for batch ops | PRD-01 Section 7.1 |
### Modifications
| ID | Previous | Updated | Reason |
|----|----------|---------|--------|
| EARS.01.25.07 | "within 100ms" | "within 50ms" | Performance threshold tightened |
### No Deletions (See No-Deletion Policy)
Version Update:
# Tier 2: Increment minor version
# 1.0.1 -> 1.1.0
current_version = "1.0.1"
new_version = increment_version(current_version, "minor") # "1.1.0"
Tier 3: Archive and Regenerate (> 15% Drift)
Trigger Conditions:
- Major PRD restructuring
- Feature scope changes
- Fundamental requirement changes
- More than 15% content drift
Archive Process:
- Create Archive Manifest:
---
title: "EARS-NN Archive Manifest"
tags:
- ears
- archive
- drift-management
custom_fields:
archive_date: "2026-02-10T16:00:00"
archived_version: "1.1.0"
drift_percentage: 23.5
trigger: "Tier 3 - Major Drift"
upstream_source: "PRD-01.md v2.0.0"
---
# EARS-NN Archive Manifest
## Archive Details
| Field | Value |
|-------|-------|
| Archive Date | 2026-02-10T16:00:00 |
| Archived Version | 1.1.0 |
| Archive Location | `archive/EARS-01_v1.1.0_20260210/` |
| Drift Percentage | 23.5% |
| Reason | Major PRD restructuring |
## Archived Files
| File | Original Location | Archive Location |
|------|-------------------|------------------|
| EARS-01.md | docs/03_EARS/ | archive/EARS-01_v1.1.0_20260210/ |
| EARS-01.1_core.md | docs/03_EARS/EARS-01_slug/ | archive/EARS-01_v1.1.0_20260210/ |
| EARS-01.2_requirements.md | docs/03_EARS/EARS-01_slug/ | archive/EARS-01_v1.1.0_20260210/ |
## Regeneration Instructions
1. Run `/doc-ears-autopilot PRD-01` to generate new EARS
2. New version will be 2.0.0
3. Review archived statements for potential reuse
4. Update downstream BDD references
- Move to Archive:
def archive_ears_document(ears_path: str, version: str, drift_pct: float) -> str:
"""
Archive EARS document before regeneration.
Args:
ears_path: Path to EARS document
version: Current version being archived
drift_pct: Drift percentage that triggered archive
Returns:
Archive directory path
"""
timestamp = datetime.now().strftime('%Y%m%d')
archive_dir = f"archive/EARS-{doc_num}_v{version}_{timestamp}/"
# Create archive directory
os.makedirs(archive_dir, exist_ok=True)
# Copy all EARS files
for ears_file in glob(f"{ears_dir}/EARS-{doc_num}*"):
shutil.copy2(ears_file, archive_dir)
# Generate archive manifest
generate_archive_manifest(archive_dir, version, drift_pct)
return archive_dir
- Trigger Regeneration:
# Invoke doc-ears-autopilot with major version increment
regeneration_params = {
"source_prd": "PRD-01",
"version_override": "2.0.0",
"archive_reference": archive_dir,
"preserve_custom_statements": True
}
Version Update:
# Tier 3: Increment major version
# 1.1.0 -> 2.0.0
current_version = "1.1.0"
new_version = increment_version(current_version, "major") # "2.0.0"
No-Deletion Policy
Critical: EARS requirements are NEVER deleted during auto-merge. Items removed from upstream PRD are marked as deprecated.
Deprecation Marker Format:
### EARS.01.25.05 [DEPRECATED]
> **Deprecation Date**: 2026-02-10
> **Reason**: Removed from PRD-01 v1.3.0 (Section 4.2 deleted)
> **Downstream Impact**: BDD-01.2 scenarios affected
**Original Statement**:
When the user initiates export, the system shall generate CSV output within 5 seconds.
**Status**: Deprecated - Do not implement. Retained for traceability.
Deprecation Tracking:
def mark_deprecated(
ears_id: str,
reason: str,
upstream_ref: str,
downstream_refs: list[str]
) -> None:
"""
Mark EARS requirement as deprecated instead of deleting.
Args:
ears_id: EARS ID to deprecate (e.g., "EARS.01.25.05")
reason: Reason for deprecation
upstream_ref: PRD reference that triggered deprecation
downstream_refs: List of downstream BDD references affected
"""
deprecation_block = f"""
### {ears_id} [DEPRECATED]
> **Deprecation Date**: {datetime.now().strftime('%Y-%m-%d')}
> **Reason**: {reason}
> **Upstream Reference**: {upstream_ref}
> **Downstream Impact**: {', '.join(downstream_refs) if downstream_refs else 'None identified'}
**Status**: Deprecated - Do not implement. Retained for traceability.
"""
# Insert deprecation block, preserve original statement
Enhanced Drift Cache
After processing drift issues, update .drift_cache.json:
{
"ears_document": "EARS-01",
"ears_version": "1.1.0",
"ears_updated": "2026-02-10T16:00:00",
"drift_reviewed": "2026-02-10T16:00:00",
"upstream": {
"document": "PRD-01.md",
"version": "1.3.0",
"last_sync": "2026-02-10T16:00:00"
},
"downstream": {
"documents": ["BDD-01.md"],
"notification_sent": "2026-02-10T16:05:00"
},
"section_hashes": {
"PRD-01#section-3": "a1b2c3d4e5f6...",
"PRD-01#section-5": "g7h8i9j0k1l2...",
"PRD-01#section-7": "m3n4o5p6q7r8..."
},
"merge_history": [
{
"date": "2026-02-10T16:00:00",
"tier": 2,
"drift_percentage": 8.3,
"version_before": "1.0.1",
"version_after": "1.1.0",
"additions": ["EARS.01.25.13", "EARS.01.25.14", "EARS.01.26.05"],
"modifications": ["EARS.01.25.07"],
"deprecations": []
}
],
"acknowledged_drift": [
{
"document": "PRD-01.md",
"section": "section-8",
"acknowledged_date": "2026-02-08",
"reason": "Informational section - no EARS impact"
}
]
}
Drift Acknowledgment Workflow
When drift is flagged but no EARS update is needed:
- Run
/doc-ears-fixer EARS-01 --acknowledge-drift - Fixer prompts: "Review drift for PRD-01.md Section 8?"
- User confirms no EARS changes needed
- Fixer adds to
acknowledged_driftarray - Future reviews skip this drift until upstream changes again
Downstream Notification
After any auto-merge, notify downstream BDD documents:
def notify_downstream(ears_id: str, changes: list[dict]) -> None:
"""
Create notification for downstream BDD documents.
Args:
ears_id: EARS document ID (e.g., "EARS-01")
changes: List of changes made during auto-merge
"""
notification = {
"type": "UPSTREAM_CHANGE",
"source": ears_id,
"timestamp": datetime.now().isoformat(),
"changes": changes,
"action_required": "Run /doc-bdd-fixer to sync"
}
# Write to .downstream_notifications.json
write_notification(f"docs/04_BDD/.downstream_notifications.json", notification)
Command Usage
Basic Usage
# Fix EARS based on latest review
/doc-ears-fixer EARS-01
# Fix with explicit review report (legacy)
/doc-ears-fixer EARS-01 --review-report EARS-01.R_review_report_v001.md
# Fix with explicit audit report (preferred)
/doc-ears-fixer EARS-01 --review-report EARS-01.A_audit_report_v001.md
# Fix and re-run review
/doc-ears-fixer EARS-01 --revalidate
# Fix with iteration limit
/doc-ears-fixer EARS-01 --revalidate --max-iterations 3
Options
| Option | Default | Description |
|---|---|---|
--review-report |
latest | Specific audit/review report to use (.A_audit_report preferred) |
--revalidate |
false | Run reviewer after fixes |
--max-iterations |
3 | Max fix-review cycles |
--fix-types |
all | Specific fix types (comma-separated) |
--create-missing |
true | Create missing reference files |
--backup |
true | Backup EARS before fixing |
--dry-run |
false | Preview fixes without applying |
--acknowledge-drift |
false | Interactive drift acknowledgment mode |
--update-drift-cache |
true | Update .drift_cache.json after fixes |
--auto-merge-tier |
auto | Force specific tier (1, 2, 3, or auto) |
--skip-archive |
false | Skip archiving for Tier 3 (regenerate in-place) |
--notify-downstream |
true | Send notifications to downstream BDD documents |
--preserve-deprecated |
true | Keep deprecated items (no-deletion policy) |
Report Selection Precedence:
- Select latest report by timestamp.
- If timestamps are equal, prefer
EARS-NN.A_audit_report_vNNN.mdoverEARS-NN.R_review_report_vNNN.md.
Fix Types
| Type | Description |
|---|---|
missing_files |
Create missing glossary, pattern, constraint docs |
broken_links |
Fix link paths |
element_ids |
Convert invalid/legacy element IDs |
content |
Fix placeholders, dates, names |
references |
Update traceability and cross-references |
drift |
Handle upstream drift detection issues |
auto_merge |
Apply tiered auto-merge for upstream changes |
deprecate |
Mark removed upstream items as deprecated |
all |
All fix types (default) |
Output Artifacts
Fix Report
Nested Folder Rule: ALL EARS use nested folders (EARS-NN_{slug}/) regardless of size. Fix reports are stored alongside the EARS document in the nested folder.
File Naming: EARS-NN.F_fix_report_vNNN.md
Location: Inside the EARS nested folder: docs/03_EARS/EARS-NN_{slug}/
Structure:
---
title: "EARS-NN.F: Fix Report v001"
tags:
- ears
- fix-report
- quality-assurance
custom_fields:
document_type: fix-report
artifact_type: EARS-FIX
layer: 3
parent_doc: EARS-NN
source_review: EARS-NN.A_audit_report_v001.md
fix_date: "YYYY-MM-DDTHH:MM:SS"
fix_tool: doc-ears-fixer
fix_version: "1.0"
---
# EARS-NN Fix Report v001
## Summary
| Metric | Value |
|--------|-------|
| Source Review | EARS-NN.A_audit_report_v001.md (or legacy `EARS-NN.R_review_report_v001.md`) |
| Issues in Review | 12 |
| Issues Fixed | 10 |
| Issues Remaining | 2 (manual review required) |
| Files Created | 2 |
| Files Modified | 4 |
## Files Created
| File | Type | Location |
|------|------|----------|
| EARS-00_GLOSSARY.md | Requirements Glossary | docs/03_EARS/ |
| EARS-01_PATTERN_LIB.md | Pattern Library Placeholder | docs/03_EARS/ |
## Fixes Applied
| # | Issue Code | Issue | Fix Applied | File |
|---|------------|-------|-------------|------|
| 1 | REV-L001 | Broken glossary link | Created EARS-00_GLOSSARY.md | EARS-01.3_constraints.md |
| 2 | REV-L004 | Broken PRD reference | Updated path to ../02_PRD/PRD-01.md | EARS-01.1_core.md |
| 3 | REV-N004 | Element type 01 invalid | Converted to type 25 | EARS-01.1_core.md |
| 4 | REV-L003 | Absolute path used | Converted to relative | EARS-01.2_requirements.md |
## Issues Requiring Manual Review
| # | Issue Code | Issue | Location | Reason |
|---|------------|-------|----------|--------|
| 1 | REV-E001 | Missing EARS keyword | EARS-01.2:L45 | Requirement syntax needed |
| 2 | REV-E002 | Invalid pattern structure | EARS-01.2:L120 | Pattern refinement required |
## Validation After Fix
| Metric | Before | After | Delta |
|--------|--------|-------|-------|
| Review Score | 92 | 97 | +5 |
| Errors | 2 | 0 | -2 |
| Warnings | 4 | 1 | -3 |
## Next Steps
1. Complete EARS-01_PATTERN_LIB.md placeholder
2. Address missing EARS keywords in flagged requirements
3. Run `/doc-ears-reviewer EARS-01` to verify fixes
Integration with Autopilot
This skill is invoked by doc-ears-autopilot in the Review -> Fix cycle:
flowchart LR
subgraph Phase5["Phase 5: Review & Fix Cycle"]
A[doc-ears-reviewer] --> B{Score >= 85?}
B -->|No| C[doc-ears-fixer]
C --> D{Iteration < Max?}
D -->|Yes| A
D -->|No| E[Flag for Manual Review]
B -->|Yes| F[PASS]
end
Autopilot Integration Points:
| Phase | Action | Skill |
|---|---|---|
| Phase 5a | Run initial review | doc-ears-reviewer |
| Phase 5b | Apply fixes if issues found | doc-ears-fixer |
| Phase 5c | Re-run review | doc-ears-reviewer |
| Phase 5d | Repeat until pass or max iterations | Loop |
Error Handling
Recovery Actions
| Error | Action |
|---|---|
| Audit/review report not found | Prompt to run doc-ears-audit or doc-ears-reviewer first |
| Cannot create file (permissions) | Log error, continue with other fixes |
| Cannot parse report | Abort with clear error message |
| Max iterations exceeded | Generate report, flag for manual review |
| PRD not found | Log warning, skip PRD-dependent fixes |
Backup Strategy
Before applying any fixes:
- Create backup in
tmp/backup/EARS-NN_YYYYMMDD_HHMMSS/ - Copy all EARS files to backup location
- Apply fixes to original files
- If error during fix, restore from backup
Related Skills
| Skill | Relationship |
|---|---|
doc-ears-audit |
Preferred combined audit report input |
doc-ears-reviewer |
Provides review report (input) |
doc-ears-autopilot |
Orchestrates Review -> Fix cycle |
doc-ears-validator |
Structural validation |
doc-naming |
Element ID standards |
doc-ears |
EARS creation rules |
doc-prd-reviewer |
Upstream PRD validation |
Version History
| Version | Date | Changes |
|---|---|---|
| 2.2 | 2026-02-26 | Migrated frontmatter to metadata; added compatibility for EARS-NN.A_audit_report_vNNN.md (preferred) with legacy EARS-NN.R_review_report_vNNN.md; defined deterministic precedence (latest timestamp, then .A_ over .R_ on ties) |
| 2.1 | 2026-02-11 | Structure Compliance: Added Phase 0 for nested folder rule enforcement (REV-STR001-STR003); Runs FIRST before other fix phases |
| 2.0 | 2026-02-10 | Enhanced Phase 6 with tiered auto-merge system; Tier 1 (< 5%): auto-merge additions with patch version; Tier 2 (5-15%): auto-merge with changelog and minor version; Tier 3 (> 15%): archive + regenerate with major version; No-deletion policy with deprecation markers; Auto-generated IDs (EARS.NN.TT.SS pattern); Enhanced drift cache with merge history; Downstream BDD notification system |
| 1.0 | 2026-02-10 | Initial skill creation; 6-phase fix workflow; Glossary and pattern file creation; Element ID conversion for EARS codes (25, 26); Broken link fixes; PRD drift detection; EARS pattern syntax validation; Integration with autopilot Review->Fix cycle |
Implementation Plan Consistency (IPLAN-004)
- Treat plan-derived outputs as valid source mode and verify intent preservation from implementation plan scope/objectives.
- Validate upstream autopilot precedence assumption:
--iplan > --ref > --prompt. - Flag objective/scope conflicts between plan context and artifact output as blocking issues requiring clarification.
- Do not introduce legacy fallback paths such as
docs-v2.0/00_REF.
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-03-11
- Index updated
- 2026-03-11
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!