10 products pages with Gemini premium CSS applied (marker DOCTRINE-201 verified): - leadforge (52279B) academy (38428) consulting (30061) ai-sdr (29446) - arsenal (47227) auditai (37500) academy-elearning (20999) - ecosysteme-ia-maroc (21032) roi-calculator (24168) linkedin-manager (25793) All HTTP 200 confirmed, Playwright audit tr:0 br:0 ZERO overlap regression Handler v2 improvements (doctrine 203): - wgux-apply.py: sudo chattr -i/+i (fix silent failure batch mode) - Verify post-apply: marker presence + size delta > 0 - Restore from GOLD backup if corruption detected - fallback sudo tee if direct write PermissionError Scripts deployed: - /var/www/html/api/wevia-gemini-ux-apply.sh (orchestrator) - /var/www/html/api/wgux-build-payload.py (Gemini prompt builder, maxTokens 16000) - /var/www/html/api/wgux-parse.py (robust JSON parser) - /var/www/html/api/wgux-apply.py v2 (sudo chattr + verify) - /var/www/html/api/wgux-shot.js (Playwright screenshot) Intents LIVE: - intent-opus4-wevia_gemini_ux_fix (review mode) - intent-opus4-wevia_gemini_ux_apply (apply mode) 10 NL triggers each: gemini ux, refais ux, apply ux gemini, audit ux gemini, etc. Gap batch reliability identified (phase 62-64): - Direct call sudo wgux-apply.py WORKS - Orchestrator via nohup sudo bash -c WORKS in foreground - Background batch parallel: sporadic silent failure despite sudo chattr - Root cause: sudo context loss in nested child process under FPM - Recommendation next phase: appel seq direct sans orchestrator BG Cumul session Opus: - 62 tags (incluant phase 65) - 42 doctrines (146-203) - 428 pages UX doctrine 60 - 10 pages Gemini premium CSS APPLIED E2E - NR 153/153 invariant 65 phases
89 lines
2.7 KiB
Python
Executable File
89 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Apply Gemini CSS patch v2 - sudo chattr + verify post-apply"""
|
|
import sys, json, os, shutil, subprocess, time
|
|
|
|
plan_path = sys.argv[1]
|
|
target = sys.argv[2]
|
|
ts = sys.argv[3]
|
|
|
|
with open(plan_path) as f:
|
|
d = json.load(f)
|
|
|
|
if not d.get('ok'):
|
|
print("NOT_OK")
|
|
sys.exit(1)
|
|
|
|
plan = d.get('plan', {})
|
|
css = plan.get('css', '')
|
|
safe = plan.get('safe', False)
|
|
|
|
if not css or not safe:
|
|
print(f"NO_CSS_OR_NOT_SAFE css_len={len(css)} safe={safe}")
|
|
sys.exit(1)
|
|
|
|
marker_start = f"<!-- DOCTRINE-201-GEMINI-APPLY-{ts} -->"
|
|
marker_end = "<!-- END-DOCTRINE-201 -->"
|
|
|
|
try:
|
|
with open(target) as f: html = f.read()
|
|
except PermissionError:
|
|
print(f"PERM_READ_FAIL {target}")
|
|
sys.exit(1)
|
|
|
|
if 'DOCTRINE-201-GEMINI-APPLY' in html:
|
|
print("ALREADY")
|
|
sys.exit(0)
|
|
|
|
if '</head>' not in html:
|
|
print("NO_HEAD")
|
|
sys.exit(1)
|
|
|
|
# GOLD backup
|
|
page = os.path.basename(target).replace('.html', '')
|
|
backup = f"/var/www/html/vault-gold/opus/{page}.html.doctrine201-apply-{ts}.bak"
|
|
os.makedirs('/var/www/html/vault-gold/opus', exist_ok=True)
|
|
shutil.copyfile(target, backup)
|
|
size_before = os.path.getsize(target)
|
|
|
|
# Clean CSS
|
|
if '<style' not in css:
|
|
css = f'<style>{css}</style>'
|
|
|
|
full = f"\n{marker_start}\n{css}\n{marker_end}\n"
|
|
new_html = html.replace('</head>', full + '</head>', 1)
|
|
|
|
# Unlock with SUDO (critical fix)
|
|
r1 = subprocess.run(['sudo', 'chattr', '-i', target], capture_output=True, text=True)
|
|
unlocked = (r1.returncode == 0)
|
|
|
|
# Write via sudo tee if direct write fails
|
|
try:
|
|
with open(target, 'w') as f:
|
|
f.write(new_html)
|
|
write_ok = True
|
|
except PermissionError:
|
|
# Fallback via sudo tee
|
|
p = subprocess.run(['sudo', 'tee', target], input=new_html, capture_output=True, text=True)
|
|
write_ok = (p.returncode == 0)
|
|
|
|
# Relock with SUDO
|
|
r2 = subprocess.run(['sudo', 'chattr', '+i', target], capture_output=True, text=True)
|
|
|
|
# VERIFY post-apply
|
|
size_after = os.path.getsize(target)
|
|
with open(target) as f: final_html = f.read()
|
|
marker_present = 'DOCTRINE-201-GEMINI-APPLY' in final_html
|
|
|
|
if marker_present and size_after > size_before:
|
|
print(f"APPLIED size_before:{size_before} size_after:{size_after} delta:+{size_after - size_before} backup:{backup}")
|
|
sys.exit(0)
|
|
else:
|
|
print(f"APPLY_FAIL marker:{marker_present} size_before:{size_before} size_after:{size_after} unlocked:{unlocked} write_ok:{write_ok}")
|
|
# Restore from backup if corrupted
|
|
if not marker_present and size_after != size_before:
|
|
subprocess.run(['sudo', 'chattr', '-i', target], capture_output=True)
|
|
shutil.copyfile(backup, target)
|
|
subprocess.run(['sudo', 'chattr', '+i', target], capture_output=True)
|
|
print(f"RESTORED_FROM_BACKUP")
|
|
sys.exit(1)
|