Skip to content

feat(publish): strip pipeline scaffolding from body before WP publish#5

Merged
ucguy4u merged 21 commits into
mainfrom
fix/publish-pipeline-clean-output
May 4, 2026
Merged

feat(publish): strip pipeline scaffolding from body before WP publish#5
ucguy4u merged 21 commits into
mainfrom
fix/publish-pipeline-clean-output

Conversation

@ucguy4u

@ucguy4u ucguy4u commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add sanitize_body_for_publish() — strips three H2 sections that are pipeline metadata, never reader content: Executed Tutorial Steps (JSON command manifest), Execution Proof (exit-code block), Screenshot Plan for the Blog Pipeline
  • H1/H2-aware boundary detection (won't overshoot across an H1 between the stripped H2 and the next H2). Idempotent. Case-insensitive title match.
  • 12 unit tests covering H2 strip, H1 boundary, EOF section, H3 collisions, idempotency, case-insensitive match.

Why

Post 796 (`git-worktree-ai-agents`) shipped to WP with raw JSON manifest + exec-proof block visible to readers. Operational hand-fix unblocked that post. This PR adds the durable pipeline guard.

Scope

This PR is intentionally narrow: sanitizer module + tests + plan doc. Deferred to follow-ups (entangled with sandbox + engagement WIP on this worktree):

  • Orchestrator hookup (call sanitizer before WP publish)
  • Hero-image picker preferring `output_.png` over `terminal_.png`
  • Sandbox tutorial skip-empty-output rule

See `docs/superpowers/plans/2026-05-04-publish-pipeline-clean-output.md` for the full plan.

Test plan

  • `pytest tests/test_publish_sanitizer.py` — 12 passing
  • Smoke test against real draft (`docs/blog-drafts/2026-05-03-git-worktree-ai-agents.md`): scaffolding stripped (10469→6714 chars), all reader sections preserved
  • After orchestrator hookup lands: end-to-end `brewpress approve-publish` against staging WP confirms sanitized body reaches `WordPressClient.publish()`

🤖 Generated with Claude Code

uday chauhan and others added 21 commits April 7, 2026 02:17
- agent_base.py: BaseAgent with use() (tool dispatch) and think() (sole
  LLM entry point). Skill files drive system prompts. Lazy LLM init.
- tools/__init__.py: @register decorator + call() for named tool dispatch.
  Auto-imports seo and content modules on first load.
- tools/seo.py: 6 pure-Python SEO tools (title, meta, keywords, headings,
  code_blocks, full). seo.full returns score 0-100.
- tools/content.py: 6 pure-Python content tools (word_count, headings,
  code_blocks, hook, paragraphs, structure_summary).
- skills/draft.md: WriterAgent system prompt v1.0 (narrative arc only).
- skills/critic.md: CriticAgent system prompt v1.0 (4-dimension scoring).
- skills/boost.md: BlogBoostAgent system prompt v1.0.
- TODOS.md: added PIPE-1 (pipeline mode flag) and SEO-1 (consolidate
  boost_eval.py) as deferred work items.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add _StructurerSchema (BaseModel) to structurer_agent.py + max_output_tokens=8192
- Add _SEOSchema (BaseModel) to seo_agent.py + max_output_tokens=8192
- Add _WriterSchema (BaseModel) to writer_agent.py (prev fix) + max_output_tokens=8192
- Add _CriticSchema (BaseModel) to critic_agent.py (prev fix) + max_output_tokens=2048
- Add BREWPRESS_MODEL env override to agent_base.py

Gemini returns unescaped control chars in markdown strings without structured output.
response_schema forces proper JSON escaping for all agents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Writer → Structurer → SEO → Critic with automatic revision loop (max 3
attempts). Orchestrator now accepts PipelineAgents injection. SEOAgent
fast-paths score>=85 on first pass. skills/seo.md and skills/structurer.md
added. revision_attempt field added to BlogJob. CriticAgent refactored to
extend BaseAgent. tenacity retry added to BaseAgent for 429/5xx. Tests
updated for new injection pattern and structured output schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Agent

71 new tests covering skill loading, tool dispatch, LLM lazy init,
fast paths, revision loop semantics, and JSON extraction edge cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
draft_agent.py and test_draft_agent.py (918 lines total) are the old
single-agent implementation. WriterAgent is the direct replacement.
No remaining imports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 8 new tests for BaseAgent tenacity retry: 429/503 retryable, 400 not,
  succeeds after 2 failures, reraises after 3 consecutive failures
- 2 new CLI tests: pipeline_summary printed when non-empty, not printed
  when empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…idated

CLI-1 (auto-approve wiring) and CLI-3 (pipeline_summary output) are fixed
by the 4-agent pipeline stack. PIPE-1 (legacy DraftAgent fallback) is
invalidated because DraftAgent was deleted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
response_schema= forces clean JSON from Gemini — no fence stripping needed.
Removes ~80 lines of dead code across writer, structurer, seo, and critic agents.
Removes 8 obsolete _extract_json tests from the test suite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eterministic critic scores

GAP-2: Add tags, categories, is_single_topic, quality_score, quality_gaps to _WriterSchema.
  These fields are read in _parse() but were missing from the Gemini structured output schema —
  causing WP posts to silently get no tags or categories.

GAP-3: Add seo_score to BlogJob. SEOAgent stamps it on every returned job (fast path and LLM
  path). CriticAgent maps it to seo_quality 1–5 via _seo_score_to_quality(), overriding the
  LLM-derived value deterministically.

GAP-4: CriticAgent computes publish_readiness from draft_body_md signals (word count, headings,
  code blocks, CTA) via _compute_publish_readiness(). Always overrides LLM-derived value.
  Eliminates 2 dimensions of LLM hallucination in scoring.

631 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both are M-effort items that eliminate 0-1 LLM calls per pipeline run each.
Deferred post-GA — structure/SEO issues are rule-based and safe to tackle after
the 4-agent pipeline proves stable in production.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RFC-style design covering:
- Complete pipeline architecture (6 agents + engagement layer)
- EngagementAgent composition (structural checker, fixer, technical checker, gate)
- Deterministic quality checks with semantic validation
- BlogJob schema with versioning, execution tracking, learning metadata
- Error handling: retry logic, 'unknown' state routing, fallback strategies
- Cost optimization gates (skip conditions, token limits)
- Logging & observability (component-level, metrics segmentation)
- Learning layer with knowledge base + post-publish feedback loop
- Security: sanitization, idempotency, WordPress deduplication
- Complete example walkthrough using Git Worktree blog
- Deployment considerations and success criteria

Example use case: Git Worktree blog (reference only; system is domain-agnostic)
Status: Ready for implementation planning
13 tasks across 4 phases:

Phase 1 (Foundation):
  - Task 1: Extend BlogJob schema with engagement metadata
  - Task 2: StructuralChecker tool (semantic validation)
  - Task 3: EngagementFixer tool (auto-improvement)
  - Task 4: TechnicalChecker tool (code validation)
  - Task 5: PublishGate tool (decision engine)

Phase 2 (Integration):
  - Task 6: EngagementAgent (composed agent)
  - Task 7: Sanitizer tool (security gate)
  - Task 8: Wire into Orchestrator

Phase 3 (Resilience):
  - Task 9: Retry logic with exponential backoff
  - Task 10: Observability (logging + metrics)

Phase 4 (Learning):
  - Task 11: Knowledge base implementation
  - Task 12: End-to-end test (Git Worktree example)
  - Task 13: Documentation

Each task is TDD (failing test → implementation → passing test → commit).
Includes exact file paths, code snippets, bash commands, expected outputs.
Complete and ready for execution.
Add EngagementScoreData, VersioningInfo, ExecutionData, LearningData,
PublishingData, PostPublishMetrics, OverrideData models.
Extend BlogJob with engagement_data, versioning, execution, learning,
publishing, post_publish, override fields.

All models use Pydantic with sensible defaults:
- EngagementScoreData: tracks structural/technical scores and publish decision
- VersioningInfo: captures model version and timestamps
- ExecutionData: tracks retries, failures, duration
- LearningData: stores content metadata (hook style, CTA type, complexity)
- PublishingData: WordPress state (post ID, slug, status)
- PostPublishMetrics: engagement metrics (CTR, reads, shares)
- OverrideData: audit trail for manual approvals

Tests verify schema composition and default values.
Add 7 engagement-related Pydantic models (VersioningInfo, ExecutionData,
LearningData, EngagementScoreData, PublishingData, PostPublishMetrics,
OverrideData) and PublishDecision enum in engagement_models.py. Extend
BlogJob with 7 new fields for tracking versioning, execution, learning,
engagement scores, publishing, post-publish metrics, and overrides.

All 3 engagement model tests pass.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add frozen=True to all 7 engagement models via ConfigDict
- Create 3 Enum classes: HookStyle, CTAType, CodeComplexity
- Convert LearningData string fields to use enums
- Add Field(ge=0, le=100) validation to structural_score, technical_score, final_score
- Expand test coverage: frozen immutability, score bounds, enum values, edge cases
- Export new enums from models/__init__.py
- All 8 tests pass; frozen and validation working as expected

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
New module sanitize_body_for_publish() removes three H2 sections that are
pipeline metadata, never reader content:
  - Executed Tutorial Steps (JSON command manifest)
  - Execution Proof (exit-code block)
  - Screenshot Plan for the Blog Pipeline

H1/H2-aware boundary detection (won't overshoot across an H1 between the
stripped H2 and the next H2). Idempotent. Case-insensitive title match.
Only H2-level scaffolding titles are stripped; matching H1 left intact.

Triggered by post 796 (git-worktree-ai-agents) shipping with raw JSON
manifest + exec-proof block visible to readers.

Scope: sanitizer module + 12 unit tests + plan doc only.
Deferred to follow-up PRs (entangled with sandbox + engagement WIP):
  - Orchestrator hookup (call sanitizer before publish)
  - Hero-image picker preferring output_*.png over terminal_*.png
  - Sandbox tutorial skip-empty-output rule

See docs/superpowers/plans/2026-05-04-publish-pipeline-clean-output.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ucguy4u
ucguy4u merged commit 0867ed4 into main May 4, 2026
1 of 2 checks passed
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.

1 participant