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
50 lines
2.0 KiB
Python
Executable File
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)
|