scripts-renumber-adrs
#!/usr/bin/env python3 """
title: "ADR Renumbering Script" component_type: script version: "1.0.0" audience: contributor status: stable summary: "Renumber ADRs to sequential format and update all references" keywords: ['adr', 'renumber', 'standardization', 'references'] tokens: ~500 created: 2025-12-22 updated: 2025-12-22 script_name: "renumber-adrs.py" language: python executable: true usage: "python3 scripts/renumber-adrs.py [--dry-run]" python_version: "3.10+" dependencies: [] modifies_files: true network_access: false requires_auth: false
Renumber ADRs to sequential format per ADR-RENUMBERING-PLAN.md.
Usage: python3 scripts/renumber-adrs.py --dry-run # Preview changes python3 scripts/renumber-adrs.py # Execute renumbering python3 scripts/renumber-adrs.py --report # Generate report only
Author: AZ1.AI INC """
import argparse import re import subprocess import sys from pathlib import Path from typing import Dict, List, Tuple
ADR renumbering mapping per ADR-RENUMBERING-PLAN.md
RENUMBER_MAP = { # Core Architecture (001-019) 'ADR-001-async-task-executor-refactoring': 'ADR-001-async-task-executor-refactoring', # no change 'ADR-002-hybrid-deployment-architecture': 'ADR-002-hybrid-deployment-architecture', # no change 'ADR-002-ADDENDUM-LICENSE-VALIDATION-STRATEGY': 'ADR-003-license-validation-strategy', 'ADR-003-user-registration-authentication': 'ADR-004-user-registration-authentication', 'ADR-006-WORK-ITEM-HIERARCHY': 'ADR-005-work-item-hierarchy', 'ADR-010-AUTONOMOUS-ORCHESTRATION-SYSTEM': 'ADR-006-autonomous-orchestration-system', 'ADR-011-UNCERTAINTY-QUANTIFICATION-FRAMEWORK': 'ADR-007-uncertainty-quantification-framework', 'ADR-012-MOE-ANALYSIS-FRAMEWORK': 'ADR-008-moe-analysis-framework', 'ADR-013-MOE-JUDGES-FRAMEWORK': 'ADR-009-moe-judges-framework', 'ADR-014-COMPONENT-CAPABILITY-SCHEMA': 'ADR-010-component-capability-schema', 'ADR-015-SELF-AWARENESS-FRAMEWORK': 'ADR-011-self-awareness-framework', 'ADR-016-LLM-COUNCIL-PATTERN': 'ADR-012-llm-council-pattern', 'ADR-017-AGENT-SKILLS-FRAMEWORK-ADOPTION': 'ADR-013-agent-skills-framework-adoption', 'ADR-018-AGENTIC-DOCUMENTATION-STANDARD': 'ADR-014-agentic-documentation-standard', 'ADR-100-CODITECT-MASTER-INDEX-SYSTEM': 'ADR-015-coditect-master-index-system',
# Context System (020-029)
'ADR-CX-CONTEXT-EXTRACTION': 'ADR-020-context-extraction',
'ADR-CXQ-CONTEXT-QUERY': 'ADR-021-context-query',
'ADR-CXQ-007-CODEBASE-INDEXING-EXPANSION': 'ADR-022-codebase-indexing-expansion',
'ADR-CXQ-008-SQLITE-SCALABILITY-ANALYSIS': 'ADR-023-sqlite-scalability-analysis',
'ADR-CXQ-009-MULTI-TENANT-PLATFORM-ARCHITECTURE': 'ADR-024-multi-tenant-platform-architecture',
# LMS System (030-039)
'ADR-LMS-PHASE-1': 'ADR-030-lms-phase-1',
'ADR-LMS-PHASE-2': 'ADR-031-lms-phase-2',
'ADR-LMS-004-user-authentication': 'ADR-032-lms-user-authentication',
'ADR-LMS-005-certificates': 'ADR-033-lms-certificates',
'ADR-LMS-006-scorm-xapi': 'ADR-034-lms-scorm-xapi',
'ADR-LMS-007-assessments': 'ADR-035-lms-assessments',
'ADR-LMS-008-analytics': 'ADR-036-lms-analytics',
'ADR-LMS-009-instructor-dashboard': 'ADR-037-lms-instructor-dashboard',
'ADR-LMS-010-external-integration': 'ADR-038-lms-external-integration',
# Runtime/Execution (040-049)
'ADR-TOKEN-ACCOUNTING': 'ADR-040-token-accounting',
'ADR-WORKFLOW-EXECUTOR': 'ADR-041-workflow-executor',
}
class ADRRenumberer: """Renumber ADRs and update references."""
def __init__(self, project_root: Path, dry_run: bool = False):
self.project_root = project_root
self.adr_dir = project_root / 'internal' / 'architecture' / 'adrs'
self.dry_run = dry_run
self.changes = []
def find_adr_files(self) -> List[Path]:
"""Find all ADR files."""
return sorted(self.adr_dir.glob('ADR-*.md'))
def rename_adrs(self) -> List[Tuple[str, str]]:
"""Rename ADR files according to the mapping."""
renames = []
for old_path in self.find_adr_files():
old_name = old_path.stem
if old_name in RENUMBER_MAP:
new_name = RENUMBER_MAP[old_name]
if old_name != new_name:
new_path = old_path.parent / f"{new_name}.md"
renames.append((old_path, new_path))
if not self.dry_run:
# Use git mv for history preservation
result = subprocess.run(
['git', 'mv', str(old_path), str(new_path)],
capture_output=True,
cwd=self.project_root
)
if result.returncode != 0:
print(f" ✗ Failed to rename {old_name}: {result.stderr.decode()}")
else:
print(f" ✓ {old_name} → {new_name}")
else:
print(f" [dry-run] {old_name} → {new_name}")
return renames
def update_references(self, renames: List[Tuple[str, str]]) -> int:
"""Update references in all files."""
if not renames:
return 0
# Build regex patterns for old names
patterns = []
for old_path, new_path in renames:
old_name = old_path.stem
new_name = new_path.stem
patterns.append((old_name, new_name))
# Find all markdown files to update
files_to_check = []
for pattern in ['**/*.md', 'CLAUDE.md', '.claude/**/*.md']:
files_to_check.extend(self.project_root.glob(pattern))
updates = 0
for file_path in files_to_check:
if not file_path.is_file():
continue
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
original = content
for old_name, new_name in patterns:
# Update various reference formats
content = content.replace(f'{old_name}.md', f'{new_name}.md')
content = content.replace(f'{old_name}]', f'{new_name}]')
content = content.replace(f'/{old_name}', f'/{new_name}')
if content != original:
if not self.dry_run:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
updates += 1
rel_path = file_path.relative_to(self.project_root)
print(f" {'[dry-run] ' if self.dry_run else ''}Updated: {rel_path}")
except Exception as e:
print(f" ✗ Error processing {file_path}: {e}")
return updates
def update_frontmatter(self, renames: List[Tuple[str, str]]):
"""Update adr_number in frontmatter of renamed files."""
for old_path, new_path in renames:
# Extract new number
match = re.match(r'ADR-(\d+)', new_path.stem)
if not match:
continue
new_number = match.group(1)
actual_path = new_path if not self.dry_run else old_path
if not actual_path.exists():
continue
try:
with open(actual_path, 'r', encoding='utf-8') as f:
content = f.read()
# Update adr_number in frontmatter
content = re.sub(
r'^adr_number: \d+',
f'adr_number: {new_number}',
content,
flags=re.MULTILINE
)
if not self.dry_run:
with open(actual_path, 'w', encoding='utf-8') as f:
f.write(content)
print(f" ✓ Updated frontmatter: {new_path.name}")
except Exception as e:
print(f" ✗ Error updating frontmatter {actual_path}: {e}")
def generate_report(self) -> str:
"""Generate a report of the renumbering."""
report = ["# ADR Renumbering Report\n"]
report.append(f"**Date:** {Path(__file__).stat().st_mtime}\n")
report.append("\n## Renaming Map\n")
report.append("| Old Name | New Name |")
report.append("|----------|----------|")
for old, new in RENUMBER_MAP.items():
if old != new:
report.append(f"| {old} | {new} |")
return "\n".join(report)
def main(): parser = argparse.ArgumentParser( description="Renumber ADRs to sequential format", epilog="Based on ADR-RENUMBERING-PLAN.md" ) parser.add_argument("--dry-run", action="store_true", help="Preview without changes") parser.add_argument("--report", action="store_true", help="Generate report only") args = parser.parse_args()
script_path = Path(__file__).resolve()
project_root = script_path.parent.parent
renumberer = ADRRenumberer(project_root, args.dry_run)
if args.report:
print(renumberer.generate_report())
return
if args.dry_run:
print("🔍 DRY RUN - No files will be modified\n")
print("📋 Step 1: Renaming ADR files...\n")
renames = renumberer.rename_adrs()
print(f"\n Renamed: {len(renames)} files\n")
print("📝 Step 2: Updating references...\n")
updates = renumberer.update_references(renames)
print(f"\n Updated: {updates} files\n")
print("🔧 Step 3: Updating frontmatter...\n")
renumberer.update_frontmatter(renames)
print("\n✅ ADR renumbering complete!")
if args.dry_run:
print("\n⚠️ This was a dry run. Run without --dry-run to apply changes.")
if name == "main": main()