Files
html/api/wgux-parse.py
Opus 6e240b4f31 phase65 doctrine 203 WEVIA GEMINI UX APPLY 10 PAGES PREMIUM CSS + handler v2 sudo-chattr
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
2026-04-24 18:33:06 +02:00

50 lines
2.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Parse Gemini response robustly"""
import sys, json, re, os
raw_path = sys.argv[1]
out_path = sys.argv[2]
try:
with open(raw_path) as f:
resp = json.load(f)
if 'error' in resp:
out = {"ok": False, "err": resp['error']}
else:
text = resp['candidates'][0]['content']['parts'][0]['text']
finish = resp['candidates'][0].get('finishReason', 'UNKNOWN')
# Clean markdown wrappers
text = re.sub(r'```(?:json|html|css)?\s*', '', text)
text = text.replace('```', '').strip()
# Find JSON object
m = re.search(r'\{.*\}', text, re.DOTALL)
if m:
try:
plan = json.loads(m.group(0))
out = {"ok": True, "plan": plan, "finishReason": finish}
except json.JSONDecodeError as e:
# Try to extract css field directly
css_m = re.search(r'"css"\s*:\s*"(<style[^"]*(?:\\.[^"]*)*)"', text, re.DOTALL)
if css_m:
# doctrine203-unicode-fix
try:
css_raw = json.loads('"' + css_m.group(1) + '"')
except (ValueError, json.JSONDecodeError):
css_raw = css_m.group(1).encode('utf-8', errors='replace').decode('unicode_escape', errors='ignore')
out = {"ok": True, "plan": {"css": css_raw, "safe": True, "partial": True}, "finishReason": finish, "parse_mode": "regex_rescue"}
else:
out = {"ok": False, "raw": text[:8000], "finishReason": finish, "parse_err": str(e)[:100]}
else:
out = {"ok": False, "raw": text[:8000], "finishReason": finish}
with open(out_path, 'w') as f:
json.dump(out, f, ensure_ascii=False, indent=2)
print("PARSE_OK", out.get('ok'), out.get('finishReason', ''))
except Exception as e:
print("PARSE_ERR", str(e)[:200])
with open(out_path, 'w') as f:
json.dump({"ok": False, "err": str(e)[:200]}, f)