Files
chemavx-seo-tools/phase2_apply.py
T
ChemaVXandClaude Opus 4.8 beae7c154a Initial commit: SEO rule engine, auditor, validator, exceptions, mirror
Versioning ~/seo-tools as critical SEO infrastructure (was home-dir only).
Canonical home of seo_rules.py — ResearchOwl will vendor a copy with a CI diff
guard against this repo. No secrets; Ghost access is via the ghst-en wrapper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:01:44 +00:00

52 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""
Phase 2 apply — trim 9 long meta_descriptions, keep og/twitter mirrored.
Reads new descriptions from phase2_desc.json (slug -> new meta_description).
For each post sets meta_description = og_description = twitter_description to the
SAME new value via a minimal ghst --from-json patch (+ updated_at for Ghost's
collision check). Touches nothing else. READ-ONLY against meta_title/body/etc.
"""
import json, os, subprocess, sys, tempfile
from seo_audit import fetch_posts
NEW = json.load(open(os.path.join(os.path.dirname(__file__), "phase2_desc.json")))
def apply_one(post, val):
body = {"posts": [{
"meta_description": val,
"og_description": val,
"twitter_description": val,
"updated_at": post["updated_at"],
}]}
fd, jp = tempfile.mkstemp(suffix=".json"); os.close(fd)
fd, op = tempfile.mkstemp(suffix=".json"); os.close(fd)
try:
json.dump(body, open(jp, "w"))
cmd = f"ghst-en post update {post['id']} --from-json {jp} --json > {op}"
r = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE, text=True)
if r.returncode != 0:
return r.stderr.strip()
return None
finally:
os.unlink(jp); os.unlink(op)
def main():
posts = {p["slug"]: p for p in fetch_posts()}
ok, fail = [], []
for slug, val in NEW.items():
p = posts.get(slug)
if not p:
fail.append((slug, "slug not found in live posts")); print(f" FAIL {slug}: not found"); continue
err = apply_one(p, val)
if err:
fail.append((slug, err)); print(f" FAIL {slug}: {err[:90]}")
else:
ok.append(slug); print(f" OK {slug} (meta/og/twitter desc = {len(val)} chars)")
print(f"\nApplied: {len(ok)} | Failed: {len(fail)}")
for s, e in fail:
print(f" FAILED {s}: {e}")
if __name__ == "__main__":
main()