Skip to content

Phase 2: Blog automation hardening + Instagram content pipeline#24

Merged
willyd61 merged 1 commit into
mainfrom
feature/pid-content-automation
Jul 8, 2026
Merged

Phase 2: Blog automation hardening + Instagram content pipeline#24
willyd61 merged 1 commit into
mainfrom
feature/pid-content-automation

Conversation

@willyd61

@willyd61 willyd61 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 2 — content automation. Two deliverables: hardening the blog scheduled-publish system (which had a critical page-destroying bug) and scaffolding the Instagram content pipeline from the 30-day calendar workbook.

1. Blog publisher hardening

🛑 Critical fix: index no longer self-destructs

regenerate_blog_index() previously overwrote the fully designed, SEO-optimized blog/index.html with a bare unstyled <ul> on the first scheduled publish — wiping the page the homepage blog teaser links to. It now injects styled .blog-card markup between BLOG_POSTS_START / BLOG_POSTS_END markers, preserving the designed page and existing cards.

  • Idempotent — a post already carded is skipped (verified: re-run produces no duplicates).
  • Fail-safe — if the markers are missing it does nothing rather than clobbering the page.

This was not hypothetical: the overdue placeholder draft (kitchen-paint-selection-guide, due 2026-07-01) would have triggered exactly this destruction on the next cron run.

Quality gate

New pid_is_publishable_post() blocks bare stub drafts (missing site chrome / too little copy) from going live. That overdue stub is now correctly skipped instead of published.

Dry-run

php blog/publish-scheduled.php --dry-run previews what would publish without moving or writing anything.

Verification (isolated tree)

Published a styled draft end-to-end → card injected, page integrity preserved (492 lines, GTM/SEO/existing cards intact — not the old 12-line list), idempotent on re-run, injected markup passes HTMLHint.

2. Instagram content pipeline (automation/instagram/)

Integration-agnostic scaffolding — prepares and tracks content; does not post (that needs Meta Graph API or a scheduler; documented in the README).

xlsx --parse_calendar.py--> content-calendar.json --build_queue.py--> queue.json + bundles/*.md
  • parse_calendar.py.xlsx → structured JSON, stdlib-only (no pip). Preserves every worksheet.
  • build_queue.py — calendar → status-tracked queue + per-post copy-paste bundles. Status (draft→approved→scheduled→posted) is preserved across re-runs.
  • Schema, a labeled sample calendar (pipeline runs before the real parse), README with the poster-integration guide.
  • Generated calendar/queue/bundles are git-ignored; only source is tracked.

Testing

  • ✅ HTMLHint, Stylelint, ESLint pass
  • 43/43 PHP unit tests pass (added pid_is_publishable_post + pid_render_blog_card suites, incl. XSS + path-traversal escaping)
  • ✅ Instagram pipeline run end-to-end against the sample; status-preservation verified

Action required

The authoritative content-calendar.json must be generated by re-running parse_calendar.py against the source workbook — the upload is ephemeral and could not be persisted, and the calendar can't be faithfully reconstructed from a partial capture without inventing content. Re-attach the .xlsx and it's one command to populate the full 30-day calendar.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R2zjTE7NTsrMH1FT6iwdc9


Generated by Claude Code

…pipeline

Phase 2 — content automation.

Blog publisher hardening (blog/publish-scheduled.php):
- CRITICAL FIX: regenerate_blog_index() previously OVERWROTE the fully designed,
  SEO-optimized blog/index.html with a bare unstyled <ul> on the first scheduled
  publish — destroying the page the homepage links to. It now injects styled
  .blog-card markup between BLOG_POSTS_START/END markers, preserving the page and
  existing cards. Idempotent (skips already-carded posts) and fail-safe (does
  nothing if the markers are missing rather than clobbering the page).
- Quality gate: pid_is_publishable_post() blocks bare stub drafts (missing site
  chrome / too little copy) from going live. The overdue placeholder draft that
  would previously have published-then-nuked the index is now correctly skipped.
- Added --dry-run mode to preview what would publish without moving/writing files.
- Card metadata sourced from scheduled-posts.json (title/category/description/
  date) with a category→image fallback map.

Shared helpers (forms/lib/form-helpers.php):
- New pure, testable pid_is_publishable_post() and pid_render_blog_card()
  (HTML-escaped, slug-sanitized).

Tests: added pid_is_publishable_post + pid_render_blog_card suites (XSS + path
traversal covered). 43/43 pass. Verified index injection end-to-end in an
isolated tree: card injected, page integrity preserved (492 lines, GTM/SEO/cards
intact — not the old 12-line list), idempotent on re-run, valid HTML.

Instagram content pipeline (automation/instagram/):
- parse_calendar.py: .xlsx → content-calendar.json, stdlib-only (no pip). Keeps
  every worksheet; surfaces the calendar sheet as `posts`.
- build_queue.py: calendar → queue.json + per-post copy-paste bundles/*.md, with
  a draft→approved→scheduled→posted status preserved across re-runs.
- Schema, labeled sample calendar (pipeline runs before the real parse), and a
  README documenting how to wire a real poster (Meta Graph API / scheduler).
- Generated calendar/queue/bundles are git-ignored; only source is tracked.

Note: the authoritative calendar must be generated by re-running parse_calendar.py
against the source workbook (the upload is ephemeral and could not be persisted).

All linters pass (HTMLHint/Stylelint/ESLint); 43/43 PHP unit tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2zjTE7NTsrMH1FT6iwdc9

@willyd61 willyd61 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved

@willyd61 willyd61 marked this pull request as ready for review July 8, 2026 11:06
@willyd61 willyd61 merged commit 9323549 into main Jul 8, 2026
6 checks passed
@willyd61 willyd61 deleted the feature/pid-content-automation branch July 8, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants