feat(ADM-018): completed feature

This commit is contained in:
chattie
2026-08-17 22:23:10 +02:00
parent cf1c69fc8b
commit d595b4871f
871 changed files with 47411 additions and 281 deletions

View File

@@ -0,0 +1,248 @@
#!/usr/bin/env python3
"""
Fix Orquestra violations in new-mercadodevida.
This script performs all the corrective actions identified in the audit.
"""
import json
import shutil
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BACKLOG_PATH = ROOT / 'backlog' / 'features.json'
ARTIFACTS_DIR = ROOT / 'work' / 'artifacts'
RUNTIME_STATUS = ROOT / 'work' / 'runtime-status.json'
SPECS_DIR = ROOT / 'specs'
LEGACY_DIR = ROOT / 'legacy'
def now_iso():
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z')
def fix_specs_directory():
"""Archive specs/ (plural) to legacy/"""
if not SPECS_DIR.exists():
print(" ✅ specs/ directory does not exist (already clean)")
return
print(f" 📦 Moving specs/ to legacy/specs-old/")
LEGACY_DIR.mkdir(exist_ok=True)
dest = LEGACY_DIR / 'specs-old'
if dest.exists():
shutil.rmtree(dest)
shutil.move(str(SPECS_DIR), str(dest))
print(f" ✅ Archived specs/ to legacy/specs-old/")
def fix_features_without_valid_gates(backlog):
"""Move features done without valid gates to pending"""
fixed = []
for feature in backlog.get('features', []):
if feature.get('status') != 'done':
continue
feature_id = feature.get('id')
gates = feature.get('gates', {})
# Check if gates are valid (all required gates approved)
required_gates = ['reviewer', 'security', 'qa']
has_valid_gates = all(gates.get(gate) is True for gate in required_gates)
if not has_valid_gates:
# Check if it's an old feature with 'review' instead of 'reviewer'
if gates.get('review') is True and gates.get('security') is True and gates.get('qa') is True:
# This is an old feature with correct gates but wrong nomenclature
continue
# Move to pending
feature['status'] = 'pending'
if 'completed_at' in feature:
del feature['completed_at']
fixed.append(feature_id)
return fixed
def fix_features_without_artifacts(backlog):
"""Move features done without artifacts to pending"""
fixed = []
for feature in backlog.get('features', []):
if feature.get('status') != 'done':
continue
feature_id = feature.get('id')
artifact_dir = ARTIFACTS_DIR / feature_id
if not artifact_dir.exists():
# Feature is done but has no artifacts
feature['status'] = 'pending'
if 'completed_at' in feature:
del feature['completed_at']
fixed.append(feature_id)
return fixed
def normalize_gate_nomenclature(backlog):
"""Change 'review' to 'reviewer' in features"""
normalized = []
for feature in backlog.get('features', []):
gates = feature.get('gates', {})
if 'review' in gates and 'reviewer' not in gates:
gates['reviewer'] = gates.pop('review')
feature['gates'] = gates
normalized.append(feature.get('id'))
return normalized
def add_completed_at_to_done_features(backlog):
"""Add completed_at timestamp to features that are done but missing it"""
fixed = []
for feature in backlog.get('features', []):
if feature.get('status') == 'done' and not feature.get('completed_at'):
# Use a default timestamp (we don't know when it was actually completed)
feature['completed_at'] = '2026-08-17T00:00:00Z'
fixed.append(feature.get('id'))
return fixed
def standardize_feature_schema(backlog):
"""Ensure all features have minimum required fields"""
standardized = []
for feature in backlog.get('features', []):
# Add missing fields with defaults
if 'type' not in feature:
feature['type'] = 'feature'
if 'priority' not in feature:
feature['priority'] = 'medium'
if 'gates' not in feature:
feature['gates'] = {}
standardized.append(feature.get('id'))
return standardized
def sync_runtime_status(backlog):
"""Sync runtime-status.json with features.json"""
if not RUNTIME_STATUS.exists():
print(" ⚠️ runtime-status.json does not exist")
return
try:
status = json.loads(RUNTIME_STATUS.read_text(encoding='utf-8'))
feature_id = status.get('feature_id')
if not feature_id:
print(" ✅ No active feature in runtime-status.json")
return
# Find the feature in backlog
feature_found = None
for feature in backlog.get('features', []):
if feature.get('id') == feature_id:
feature_found = feature
break
if not feature_found:
print(f" ⚠️ Feature {feature_id} in runtime-status.json not found in backlog")
return
# Check if status matches
runtime_state = status.get('state')
backlog_status = feature_found.get('status')
if backlog_status == 'pending' and runtime_state in ['running', 'done']:
print(f" 🔧 Syncing runtime-status.json: {feature_id} is pending in backlog but {runtime_state} in runtime")
status['state'] = 'waiting'
status['stage'] = 'idle'
status['action'] = 'Feature is pending in backlog'
RUNTIME_STATUS.write_text(json.dumps(status, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
print(f" ✅ Synced runtime-status.json for {feature_id}")
else:
print(f" ✅ runtime-status.json is consistent with backlog")
except Exception as e:
print(f" ❌ Error syncing runtime-status.json: {e}")
def main():
print("🔧 Fixing Orquestra violations in new-mercadodevida\n")
# Load backlog
if not BACKLOG_PATH.exists():
print(f"❌ Backlog not found: {BACKLOG_PATH}")
return 1
try:
backlog = json.loads(BACKLOG_PATH.read_text(encoding='utf-8'))
except Exception as e:
print(f"❌ Invalid backlog: {e}")
return 1
# 1. Archive specs/ directory
print("1⃣ Archiving specs/ (plural) directory...")
fix_specs_directory()
print()
# 2. Fix features without valid gates
print("2⃣ Fixing features without valid gates...")
fixed_gates = fix_features_without_valid_gates(backlog)
print(f" ✅ Moved {len(fixed_gates)} features to pending: {', '.join(fixed_gates[:10])}{'...' if len(fixed_gates) > 10 else ''}")
print()
# 3. Fix features without artifacts
print("3⃣ Fixing features without artifacts...")
fixed_artifacts = fix_features_without_artifacts(backlog)
print(f" ✅ Moved {len(fixed_artifacts)} features to pending: {', '.join(fixed_artifacts[:10])}{'...' if len(fixed_artifacts) > 10 else ''}")
print()
# 4. Normalize gate nomenclature
print("4⃣ Normalizing gate nomenclature (review → reviewer)...")
normalized = normalize_gate_nomenclature(backlog)
print(f" ✅ Normalized {len(normalized)} features: {', '.join(normalized[:10])}{'...' if len(normalized) > 10 else ''}")
print()
# 5. Add completed_at to done features
print("5⃣ Adding completed_at to done features...")
fixed_timestamps = add_completed_at_to_done_features(backlog)
print(f" ✅ Added timestamps to {len(fixed_timestamps)} features")
print()
# 6. Standardize feature schema
print("6⃣ Standardizing feature schema...")
standardized = standardize_feature_schema(backlog)
print(f" ✅ Standardized {len(standardized)} features")
print()
# 7. Sync runtime-status.json
print("7⃣ Syncing runtime-status.json with backlog...")
sync_runtime_status(backlog)
print()
# Save backlog
try:
BACKLOG_PATH.write_text(json.dumps(backlog, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
print("✅ Saved updated backlog")
except Exception as e:
print(f"❌ Cannot save backlog: {e}")
return 1
# Summary
print("\n📊 Summary:")
print(f" - Features moved to pending (no valid gates): {len(fixed_gates)}")
print(f" - Features moved to pending (no artifacts): {len(fixed_artifacts)}")
print(f" - Features normalized (review → reviewer): {len(normalized)}")
print(f" - Features with added timestamps: {len(fixed_timestamps)}")
print(f" - Features standardized: {len(standardized)}")
print("\n✅ All fixes applied successfully")
return 0
if __name__ == '__main__':
import sys
sys.exit(main())