v1.9.1 is two releases in one announcement. v1.9.0 made the blog reviewer blocking instead of advisory, so drafts under 90 of 100 stop existing on disk before they ever reach you. v1.9.1 ran the whole project through an 8-agent cybersecurity audit and closed every HIGH and MEDIUM finding in 24 hours. Both shipped this week. Both ship together because they are the same shift: the gates do the work the human used to have to do.
If you came here from the walkthrough video, the timeline is real. While I was talking through the contract, an agent was generating the Codex vs Claude sample blog on screen in another window. Ten minutes, end to end, including research, hero image, SVG charts, and a passing review against the 5 gates. That post is on the site now. Skim it first if you want to see what the contract produces before reading what the contract does.
The Blog Delivery Contract: 5 Gates Before Any Draft Reaches You
Until v1.9.0, the architecture had a real defect. The blog-reviewer agent existed. It scored drafts. It produced a scorecard. And then the orchestrator handed the draft to you anyway, regardless of the score. Sloppy drafts with no real hero image, broken og:image, SVGs that overflowed on mobile, missing PDFs, and md/html divergence all reached the user before any gate fired. The user ended up being the first reviewer.
v1.9.0 closes that gap. The contract has 5 gates. Gate 4 is the blocking one.
| Gate | What it does | Blocks? |
|---|---|---|
| 1. Capability Discovery | Enumerates MCP servers, agents, scripts, env vars before drafting. Writes capabilities.json. | No (info only) |
| 2. Format Completeness | Every draft must ship .md + .html + .pdf + a real hero image. Deterministic renderer from one source. | Yes |
| 3. Visual Verification | Playwright renders the HTML at 375, 768, and 1280 px. Asserts no SVG overflow, dark-mode swap works, console clean. | Yes |
| 4. Content Review (BLOCKING) | blog-reviewer scores the rendered HTML. Threshold: 90 of 100, zero P0 issues. Emits machine-readable BLOCKING: true|false. | Yes (hard) |
| 5. Asset + Link Integrity | Every img src resolves, og:image points to a real file, external links return 200, JSON-LD wordCount matches body within 5%. | Yes |
On any gate failure, the orchestrator iterates up to 3 times before escalating to the user with a diagnostic. You are no longer the first reviewer. The gates are.
The Hero Image Ladder: 5 Tries Before We Block
Gate 2 demands a real hero image. The old failure mode was a missing image plus a fabricated og:image URL that 404d the moment someone shared the post on LinkedIn. scripts/generate_hero.py now runs a five-step ladder:
- Banana MCP (when the nanobanana-mcp server is loaded)
- Direct Gemini API via google-genai (when
GOOGLE_AI_API_KEYis set) - Premium stock APIs (Unsplash, Pexels, Pixabay) - any key suffices
- Openverse public API (CC-licensed, no key required)
- Block with setup instructions if none of the above succeed
The block is intentional. A missing hero is a missing hero. No silent fallback to a placeholder gray rectangle that ships to production and embarrasses you on the open graph preview a week later.
v1.9.1: The 8-Agent Cybersecurity Audit Pass
The Blog Delivery Contract introduced new attack surface. generate_hero.py fetches remote images. blog_preflight.py writes provenance files into draft directories. The reviewer reads files the user created. All of that is new pipe between trusted code and the open internet.
So v1.9.1 ran 8 specialist agents against the v1.9.0 surface: OWASP Top 10:2021, CWE Top 25:2024, MITRE ATT&CK pattern matching, supply chain analysis, credential handling review, IaC drift check, AI-generated code audit, and threat-model fitness. Composite score against the audit: 90 of 100. Every HIGH and MEDIUM finding closed in v1.9.1.
What Got Closed
| ID | Severity | Fix |
|---|---|---|
VULN-801 / CHAIN-801 | HIGH (CRITICAL on cloud) | SSRF guard in generate_hero._http_get: scheme allowlist, private/loopback/link-local refuse via ipaddress, 25 MB cap, no-redirect opener. Closes the AWS-IMDS-via-og:image attack path. |
VULN-802 | HIGH | Code-enforced iteration counter at <draft>/.iteration-count. MAX_ITERATIONS = 3, exit code 2 on exceed. Was orchestrator-prose-only. |
VULN-401 | HIGH | uv.lock with 142 hash-pinned packages. Reproducible install via uv sync --frozen. |
VULN-803 / CHAIN-802 | MEDIUM | Nonce-bound reviewer provenance. _init_review_nonce writes secrets.token_hex(16), Gate 4 verifies review.md echoes the nonce. Closes counterfeit reviewer pass. |
AUTH-001 | MEDIUM | OAuth client_secret split from token blob. Re-read on construction from oauth_client_path. |
VULN-804 / VULN-002 | MEDIUM / LOW | _PreflightNoRedirectHandler refuses non-http(s) schemes inline and refuses 30x redirects on Gate 5 link checks. |
VULN-404 / 405 / 406 / 411 | MEDIUM x3 + INFO | Tightened upper bounds on weasyprint, google-genai, sentence-transformers. setuptools floor at 70.0.0 (CVE-2024-6345 RCE). |
VULN-IAC-001 | MEDIUM | SHA-256 hashes for install.sh and install.ps1 published in README. Verify integrity before piping curl into bash. |
VULN-S01 | LOW | API-key echo in setup_image_mcp.py --project masked as AIza****WXYZ. |
VULN-IAC-003 | LOW | install.sh validates skill_name against ^[a-z0-9-]+$ before mkdir / cp. Defense in depth against path-traversal in a tampered repo. |
The AUTH-002 PKCE finding stayed open as not-in-scope. Installed-app OAuth flow with explicit client_secret.json does not require PKCE; CSPRNG state plus fixed loopback redirect cover the CSRF case.
Quality Gates: Final State
- Tests: 160 to 187 (+27 targeted at the fixes)
- Prose hygiene: 0 violations
- Plugin manifest: valid
- Version coherence: all 14 surfaces at
1.9.1(4 canonical plus 10 sub-skillSKILL.md) - Dependency lock: present (
uv.lock, 142 packages, SHA-256 pinned) - Projected security composite: ~95 of 100 (up from 87 pre-fix)
What This Actually Looks Like in Production
The clearest answer is the Codex vs Claude sample blog. That post is the output of one end-to-end run on the v1.9.1 contract. Hero image generated via the ladder. SVG charts inline. JSON-LD validated. FAQ schema present. Score 92 of 100 on the blocking reviewer. Total elapsed time including research, drafting, image generation, rendering, and the full 5-gate pass: about 10 minutes. The folder it produced has the contract artifacts in it: capabilities.json, .iteration-count, .review-nonce, preflight-report.json, review.md. The same gates that produced that post produce every post.
Watch the Walkthrough
Two ways to consume v1.9.1. Flip through the 12-slide release deck for a 60-second skim, or watch the 12:48 walkthrough below for the full demo, including 6 minutes of live agent rendering.
The video shows the contract running live: cover image generation, the SVG chart pipeline, the inline embed flow, the 6-minute silent stretch in the middle is the agent rendering the Codex vs Claude post in real time. Skip to 8:17 if you want to jump straight to the install walkthrough and the finished sample.
Upgrade Guidance
If you are on v1.7.x or earlier, this is the right upgrade. Read the security FAQ first (links in the v1.7.1 post). Rotate any Google AI API keys that might have been committed to .mcp.json before v1.7.1. Then update.
If you are on v1.8.x, update straight through. The two soft-compat shims in v1.9.1 mean nothing breaks if your reviewer config is older. The shims emit a deprecation warning and will be removed in v1.10.0, so update your oauth_client_path config and let your draft regenerate the .review-nonce file once.
If you build CI around the skill, pin to v1.9.1 in your install command and use uv sync --frozen against the committed uv.lock for reproducible installs. The 142 hash-pinned packages mean PyPI mirror compromises and typosquats cannot land malicious code at install time.
Install or Update
curl -sSL https://raw.githubusercontent.com/AgriciDaniel/claude-blog/main/install.sh | bash
Verify installer integrity against the SHA-256 hashes published in the README before piping curl into bash. Reproducible alternative via git clone plus uv sync --frozen is documented in docs/INSTALLATION.md.
Conclusion
v1.8.x taught a lesson. Seven hardening waves in 48 hours, each consuming the previous round's audit output as the next round's input, lifted the security baseline from "good enough" to "audited and locked." v1.9.0 applied that lesson one layer up: from the project's own code to the project's output. v1.9.1 then re-ran the security playbook against the expanded surface. Both shipped together because they are one shift. Audits ladder you up to your asymptote. Infrastructure raises the asymptote. The reviewer is no longer the last advisory voice. It is the gate.