AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Seo Blog

skill-xuanranl-loamwright-seo-skill-seo-blog · by XuanRanL

Production-grade SEO + GEO content factory. Routes user intent to one of 5 phases (research / build / optimize / publish / monitor) or runs end-to-end. Use whenever the user wants to write a blog post, create SEO/GEO-optimized articles, refresh existing content, audit a page, or publish to WordPress. Always trigger when user mentions "blog", "article", "content", "SEO", "SERP", "AI search", "Chat…

No reviews yet
0 installs
20 views
0.0% view→install

Install

$ agentstack add skill-xuanranl-loamwright-seo-skill-seo-blog

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-xuanranl-loamwright-seo-skill-seo-blog)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Seo Blog? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

SEO Blog Writer · Master Orchestrator (v3.7)

Production-grade content factory for the Google + AI search dual front.

⛔ STOP — READ THIS FIRST: Deterministic Runner-Driven Execution (v3.14)

When the intent is /article (full pipeline), you DRIVE THE PIPELINE WITH THE RUNNER, not by hand. DO NOT read the pipeline prose in this file and decide stages yourself. DO NOT call orchestrator --action next/verify manually between stages — the RUNNER does that for you. DO NOT write artifact JSON files (fact-check.json, humanizer-report.json, review.json) manually.

Why a runner now drives (v3.14, 2026-06-03)

Before v3.14 the orchestrator was a PASSIVE state machine and the LLM had to hand-drive all ~30 stages — calling --action next, running/dispatching each stage, then --action verify — between EVERY stage, for EVERY article. That manual ritual is exactly where steps got skipped or driven out of order (2026-05-26 "23/35 skipped"; 2026-06-02 "geo skipped"; 2026-06-03 "stage records missed + heavy by-hand driving"). Enforcement made bad outcomes detectable at the gates, but execution was only as reliable as operator discipline. scripts/pipeline/run_pipeline.py now drives the loop in CODE: it runs/launches/checks every BASH/BACKGROUND/CHECK stage itself and records it, and STOPS only to hand you the handful of LLM stages that genuinely need a subagent. Your surface area drops from "orchestrate 30 stages" to "service the LLM stages I'm handed."

The Loop (execute this after state.json is created)

REPEAT:
  1. result = Bash("python -m scripts.pipeline.run_pipeline --workspace {task_id} --json"
                   [+ " --completed-llm {last_llm_stage}" if you just finished one])

  2. Read result.action:
     "COMPLETE"      → DONE. Proceed to the publish-confirmation step.
     "DISPATCH_LLM"  → Dispatch Agent(subagent_type=result.subagent_type, prompt=result.dispatch_prompt).
                       If subagent_type is "", execute result.description inline (format-selector /
                       outline-architect / meta-builder are inline LLM stages).
                       After the subagent finishes, confirm every path in result.expected_outputs exists,
                       then GOTO 1 with --completed-llm {result.stage}.
                       ⚠ TRANSIENT NO-OP (v3.38.3): a dispatched subagent can occasionally return
                       with ZERO tool uses and no output files (harness init glitch — a 2026-07-09
                       humanizer did this, returning only a garbled system-reminder). expected_outputs
                       will be missing: simply RE-DISPATCH the same subagent once before investigating
                       anything else. Same rule when a subagent dies mid-run to an API/session error
                       (a geo-auditor was killed this way in the same batch): check whether its
                       evidence artifact landed on disk; if not, re-dispatch — the draft itself is
                       usually untouched.
     "GATE_FAILED"   → A lint/quality gate found defects (result.gate). Route to repair (fix the draft
                       or re-dispatch the responsible subagent per subskills/cross-cutting/repair-orchestrator),
                       then GOTO 1 (the runner re-runs the failed stage).
     "WAIT"          → A CHECK isn't ready (e.g. Fork B image gen still running). Wait briefly, GOTO 1.
     "BLOCKED"       → A stage is missing inputs (result.missing_inputs). Fix them, GOTO 1.
     "ERROR"         → A BASH stage crashed (result.detail). Inspect, fix, GOTO 1.
     "LOCKED"        → Another run_pipeline driver is ALREADY active on this workspace (exit 30).
                       Wait for it to return, then GOTO 1. NEVER delete the .pipeline-driver.lock
                       sidecar and NEVER launch a second driver call while one is still running —
                       the 2026-07-07 batch double-published 2 of 3 posts exactly this way (a bare
                       status-check invocation re-dispatched the in-flight wordpress-publisher).

⚠️ One driver per workspace at a time (v3.36.2). The runner holds an exclusive per-workspace lock ({workspace}/.pipeline-driver.lock) for the whole invocation, so a concurrent call — including an innocent-looking bare run_pipeline --workspace X --json "status check" while a background invocation is still running — now returns LOCKED instead of double-driving. To check progress while a driver runs, read state.json :: stage_history instead.

The runner handles ALL --action next / --action verify / stage-recording / ordering internally, so a stage can no longer be silently skipped or left unrecorded. You only ever act on DISPATCHLLM, GATEFAILED, WAIT, BLOCKED, ERROR. The underlying engine is still scripts/pipeline/orchestrator.py (the deterministic artifact/evidence/provenance gatekeeper); the runner is the loop on top of it.

Three Rules That Are Not Negotiable

  1. Every DISPATCH_LLM with a subagent_type MUST be dispatched as an Agent call. You are the driver, not the writer/fact-checker/humanizer/reviewer. The independent-subagent property is load-bearing for quality.
  2. Never write a provenance-gated artifact yourself. The full set (single source of truth: scripts/_core/provenance.py, shared by orchestrator + prepublishgate since v3.41.3) is: fact-check.json, humanizer-report.json, review.json, geo-audit.json, visual-design-report.json, cta-draft.json, image-qa-report.json, internal-link-report.json. Each requires a _generated_by value only the real subagent produces.
  3. Always re-invoke the runner with --completed-llm {stage} after an LLM stage. That is how the runner verifies + records it and advances. Do not skip it.

> Manual fallback (only if run_pipeline is unavailable): the legacy hand-driven loop is > orchestrator --action next → execute → orchestrator --action verify --stage X, repeated > between every stage. The runner exists precisely so you don't have to do this by hand.

Execution-evidence enforcement (v3.12, 2026-06-02) — every stage must PROVE it ran

Root cause of the 2026-06-02 batch incident ("geo step silently skipped"): the optional LLM stages geo-content-optimizer, internal-linker, and citation-capsule-builder had NO required output artifact, so --action verify auto-passed them on the honour system. An orchestrator under context pressure recorded them "completed" without ever dispatching the subagent. Same failure class as the 2026-05-26 / 2026-05-27 incidents: enforcement was artifact-gated, but some stages produced no artifact, so they were unenforceable.

The cure — there is no longer an honour-system pass. Every work stage now declares execution evidence, and verify refuses to mark it complete without that evidence:

| Stage | Evidence the orchestrator now requires | |---|---| | geo-content-optimizer | geo-audit.json with _generated_by:"geo-auditor-subagent" | | internal-linker | internal-link-report.json (links_added, anchors[]) | | citation-capsule-builder | citation-capsule-result.json (from citation_capsule_lint --out) | | serp-analysis / competitor-analysis | non-empty serp_features / competitors keys in research.json | | (already enforced) fact-check / humanizer / independent-reviewer | provenance-stamped JSON |

--action verify on a stage whose evidence is missing now returns passed:false and exits 1. The ONLY stage still recorded on trust is the BACKGROUND image-pipeline-fork launch, whose real output (images.json) is checked later at the JOIN.

If you genuinely want to skip an optional stage, you MUST do it explicitly — never fake completion:

python -m scripts.pipeline.orchestrator --workspace {task_id} --action skip \
    --stage geo-content-optimizer --reason "client opted out of GEO pass for this batch"

This records status:"skipped" (NOT "completed") with the reason in the audit trail, so a later auditor can always tell what RAN from what was deliberately DROPPED. Skipping a mandatory stage is refused (exit 1). The rule of thumb: a stage is either (a) run, with its evidence artifact on disk, or (b) explicitly skipped with a logged reason. "Quietly recorded complete" is no longer possible — and tests/test_orchestrator_evidence_enforcement.py pins it.

When --action next returns one of these optional stages, do the work and produce the evidence artifact, OR skip it with a reason. Do not call verify expecting a free pass.

Startup sequence (runs every invocation)

  1. Source bin/preamble.md — version check + active project load
  2. Resolve active projectXS_ACTIVE_PROJECT env var WINS, then the shared file.

This is the contract that makes parallel multi-session work (see "Parallel multi-session" below). Resolve via the canonical helper, NOT a raw file read: ``bash # env-first resolution (XS_ACTIVE_PROJECT → ~/.xuanran-seo/active-project file): active_slug=$(python -c "from scripts._core import active_project as a; print(a.get_active_project() or '')") ` `python # equivalent semantics: active_slug = os.environ.get("XS_ACTIVE_PROJECT") or read("~/.xuanran-seo/active-project") if active_slug: business_context = load(f"projects/{active_slug}/business-context.json") check_freshness(business_context) # warn if >90d stale else: business_context = None ` ⚠️ Never read ~/.xuanran-seo/active-project directly to decide a NEW task's project_slug`. Always go env-first. In a parallel session, another session may have overwritten the shared file; the env var is this session's authoritative pin.

  1. Load shared context (@context/*.md files exist in plugin context/)
  2. Initialize cost ledger (read ~/.xuanran-seo/config.yaml, check daily total)
  3. Detect local-intent (v3.4.0, 2026-05-22)MANDATORY Bash invocation of scripts/_core/local_intent_runner.py after state.json exists. The wrapper reads project's business-context.json :: location.local_seo_mode, calls _detect_local_intent.py if not "off", and writes state.brief.local_mode + state.brief.location_anchor into state.json. This is the ONLY way to set these fields — never inline-Python the detection elsewhere (wiring-audit 2026-05-22 P1 found inline-pseudocode drift).

``bash # In seo-blog orchestrator (Bash invocation, NOT pseudocode): python -m scripts._core.local_intent_runner \ --task-id {state.task_id} \ --project-slug {active_project_slug or ''} \ --keyword "{state.brief.primary_keyword}" \ --json # Parse JSON output to learn local_mode + location_anchor + ambiguous status. ``

Scope is WORLDWIDE (v3.40.0): the gazetteer covers 252 countries (+ aliases like "uk"/"usa"), ~3.8k states/provinces (Ontario, Guangdong, Queensland, England, …) and ~34k world cities ≥15k population — a keyword like "chinese age-restricted products toronto", "tea wholesale guangdong" or "slope mower queensland" MUST set local_mode=true with the correct location_anchor.country. Cross-country duplicates (Vancouver BC/WA, London GB/ON) are resolved by in-keyword cue → the project's location.target_markets bias (derived inside the wrapper; override with --countries CA,GB) → population ≥2x → ambiguous=true.

If output's ambiguous=true, present disambiguation_options to the user and re-run with their pick before invoking phase-research (province-vs-city twins like bare "ontario" flag ambiguous by design). If local_seo_mode=off, the wrapper logs "skipped" and writes local_mode=false (legacy behavior; downstream skills follow non-local path).

Downstream effect: when localmode=true, subskills/plan/format-selector/SKILL.md Step 0 routes to templates/local-state-pillar.md or templates/local-city-page.md; subskills/build/section-drafter/SKILL.md passes local_mode + location_anchor + locality_signals_required + local_article_pattern to each writer; subskills/optimize/schema-generator/SKILL.md + scripts/build/schema_jsonld_builder.py emits LocalBusiness leaf + Service.areaServed (archetype A/B/C) OR OnlineStore/Organization + Article.spatialCoverage (archetype D/E); quality gates include gates.local_uniqueness.passed; verifypost check 27 enforces geo-anchor density.

HARD RULES — these override default behavior

These rules came from production failures. Violating them produced real costs (wasted draft work, unstyled live posts, missed traffic on legitimate keyword variants). Apply unconditionally.

Rule 1 — Exact-keyword fidelity (never silently dedupe)

When a user supplies a search term — including typos, grammatical variants, plurals/singulars, alternate spellings, or terms "similar" to an existing article — that term IS the SEO target. Treat it as a distinct keyword regardless of perceived overlap with prior work. SERP and AI-search engines differentiate 1000 watt led grow light from 1000 watt led grow lamps, seo content writer from seo content writter, chatgpt seo from chat gpt seo. Users searching the variant are real traffic.

Forbidden behaviors:

  • Silently substituting a similar existing article as "the same thing"
  • Suggesting the variant is a typo and rewriting against the corrected form without asking
  • Assuming a plural↔singular pair shares search intent
  • Treating a "nearby" keyword in an existing article as already-served

Required behavior:

  • Before reusing/canonicalizing/redirecting to existing content, ask the user explicitly. Default is: new keyword = new article.
  • If there is a genuine cannibalization risk (same primary search intent), the answer is differentiation by angle/format/persona, not skipping the work.
  • Per project, maintain an inventory of existing live articles (slug + focus_keyword + angle) in projects/{slug}/articles/ so the differentiation analysis is informed, not guessed.

Rule 2 — Project article CSS is mandatory at publish

If projects/{slug}/brand/article-css.css (or .min.css) exists, every new post must be wrapped with the scoped CSS at publish time. The wrapper class MUST equal the CSS scope selector — typically {slug}-pillar (e.g. project-charlie-pillar), but read the CSS file's leading selector to confirm. A wrapper-class/scope mismatch silently orphans every rule.

> Style-token projects (v3.42.0+, all 13 projects today). If projects/{slug}/brand/style-tokens.json exists, the PUBLISHED class names are per-project HMAC tokens, not the legacy names — project-charlie-pillar ships as mwxiod-1ymm61. wp_publisher._apply_project_styling rewrites body + CSS at the publish boundary; internal artifacts (draft.md, lints) keep the legacy names. Never hand-write or grep for a published class name — resolve it: python -m scripts._core.style_tokens --show {slug}. Verifying live HTML for .{slug}-pillar on a tokenized project is a check that can never pass.

The canonical wrapping pattern (Gutenberg-block compatible, matches the existing published posts on the site):


{minified CSS contents}

   

{article body HTML}

Inline `` schema blocks should be appended OUTSIDE the closing wrapper so the schema is not styled.

The phase-publish skill MUST verify the wrapper class is present in the rendered front-end HTML before declaring publish complete.

Batch-mode orchestration (multiple articles)

When the user requests N articles in one session, you MUST process them one at a time through the FULL orchestrator loop. Parallelism is allowed ONLY within a single article (e.g., parallel writer subagents for different sections, Fork B image pipeline). Cross-article parallelism of LLM stages causes context exhaustion and subagent stub shortcuts.

Parallel multi-session (multiple projects at once) — v3.14.4

The one-at-a-time rule above is about a single session. A diffe

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.