# Niwa Bloging: canonical publish-ready pipeline

Niwa creates, updates, approves, and publishes managed WordPress blog posts through one quality contract. A failed quality check is never converted into a raw fallback post.

## Runtime map

| Component | Responsibility |
| --- | --- |
| `includes/core/class-blog-quality-gate.php` | Deterministic normalization, audit, link analysis, source hash, publish eligibility, and structured quality report |
| `includes/admin-autonomy/traits/trait-tools-blog-pipeline.php` | Canonical draft creation, safe Markdown/HTML rendering, governed taxonomy execution, managed meta/assets, SEO integration, and real-post schema generation |
| `includes/admin-autonomy/traits/trait-tools-blog-posts.php` | Read tools plus compatibility create, canonical update, exact-revision approval, direct/scheduled publish gate, and rollback snapshots |
| `includes/admin-autonomy/traits/trait-tools-bloging.php` | Stock-image access and the shared cached published-content map used for internal-link suggestions/validation |
| `includes/frontend/class-blog-asset-renderer.php` | Singular-only CSS, progress JavaScript, and valid JSON-LD output from post meta |
| `admin/class-admin-bloging-settings.php` | Image providers, defaults, configured priority/money pages, rendering toggles, and explicit taxonomy-creation policy |

## Public tool contract

### `create_niwa_blog`

This is the primary writer. It accepts Markdown and always creates a draft.

The execution boundary:

1. safely humanizes and normalizes the proposal;
2. derives missing excerpt, slug, primary query, conservative intent, and audience when possible;
3. loads the cached map of real published posts, pages, and products;
4. checks topic collisions, useful depth, body hierarchy, internal links, media, metadata, taxonomy limits, claims, and render safety;
5. returns `success=false`, the structured report, and direct repairs if critical defects remain;
6. resolves taxonomy during execution only;
7. renders semantic HTML inside `.niwa-blog-article`;
8. creates a WordPress draft;
9. generates URL/date/author-sensitive schema from the stored post;
10. persists assets, SEO integration data, policy marker, source material/hash, selected links, and the quality report.

Preview is side-effect free. It never inserts taxonomy terms.

### `create_blog_post`

This is an HTML compatibility adapter to the same canonical creation boundary. It is not a raw writer, is not advertised as recovery from a quality failure, and cannot publish directly.

### `update_blog_post`

The update tool constructs the complete proposed post state before writing. It reruns the same gate and regenerates dependent content, CSS, progress JavaScript, schema, excerpt/SEO data, source hash, selected-link data, and the quality report.

`status=publish` is not accepted. An already published managed post is changed only after its proposed state passes, so a new critical defect is not written live.

### `publish_blog_post`

Preview approves one exact revision by freezing:

- post ID;
- expected status;
- current normalized content/source hash;
- `post_modified_gmt` (with local modified time as a fallback);
- quality-policy version.

Execution reloads the post and verifies every frozen value. It then reruns the current-state gate immediately before the status transition. A changed or broken draft is refused with the structured report.

Recurring schedules for the same fixed publish action are rejected. A concrete approved post may be scheduled with a one-time admin-plan action. The removed native “oldest draft” path no longer chooses an unmanaged draft at execution time.

## Structured quality report

The stable top-level shape is:

```php
array(
    'pass'              => true|false,
    'publishable'       => true|false,
    'score'             => 0..100,
    'checks_total'      => int,
    'checks'            => array(),
    'critical'          => array(),
    'warnings'          => array(),
    'autofixes'         => array(),
    'facts'             => array(),
    'link_report'       => array(),
    'taxonomy_report'   => array(),
    'render_contract'   => array(),
)
```

`checks_total` is the number of checks actually executed. Niwa does not claim “30/30” or any other fixed count. The score is the percentage of executed checks which passed. Warnings do not block; critical issues include a direct `repair` instruction.

## Quality policy

### Topic and intent

- A focus query, intent, audience, and optional destination money page are supplied or conservatively derived.
- Exact title, slug, or stored focus-query collisions with published content block creation.
- Strong token overlap is reported with conflicting IDs/URLs for human review; existing content is never automatically rewritten or deleted.
- Managed posts store the current pipeline/policy version and state hash.

### Copy and evidence

- `Niwa_AI_Humanizer` remains a safe deterministic autofixer; it is not documented as a fictional full human-quality oracle.
- The report adds structural detectors for repeated paragraph openings, monotonous paragraph rhythm, and unsupported exact percentage/money claims.
- Exact claims should carry supplied evidence URLs or be softened/removed.
- Fake customers, results, quotes, guarantees, capabilities, and statistics are forbidden by the writing skill and are not invented by the PHP pipeline.

### Structure

- The theme/post title owns the only rendered H1.
- `post_content` contains no H1.
- Body sections use H2 and subsections use H3 without skipped levels.
- A full article normally targets at least 800 useful words. Content under 300 words is a critical thin-content defect; content between 300 and 799 words is warned so a legitimately narrow intent can remain concise.
- Duplicate heading text receives deterministic unique IDs (`section`, `section-2`, and so on).

### Internal links and money pages

The quality gate and `scan_site_for_linking` use the same published-content map and link parser. Markdown and HTML links are parsed. Internal targets must match current published canonical paths.

For each selected link the report includes:

- target post ID, canonical URL/path, and type;
- anchor;
- source context;
- reason.

Full posts require at least three verified contextual internal links when the site map contains candidates. Repeated targets are warned. If an explicit `money_page_url` is supplied, the article must contain a natural link to that published target.

The content-map transient is invalidated on save, trash, untrash, and deletion of posts, pages, and products.

## Taxonomy governance

- Maximum two categories; one existing primary category is preferred.
- Maximum three tags; zero tags is valid.
- Missing terms are skipped by default.
- New terms require `allow_new_terms=true` or the explicit admin setting.
- Preview, approval, and scheduling never create terms.
- Execution reports reused, created, and skipped terms.
- Newly created terms are part of rollback and are deleted only while unused.

## Media

- A featured image is optional unless product policy says otherwise.
- A supplied featured image must be a real image attachment.
- Inline Markdown/HTML images require non-empty descriptive alt text.
- A supplied featured image requires descriptive attachment alt text and must not be duplicated inside the body.
- External inline-image hotlinks are blocked unless WordPress can resolve them to a managed attachment; authorized assets should be imported with source/license data preserved.
- Placeholders and unstable/unauthorized hotlinks are not generated by the pipeline.

## SEO, canonical, social, and schema ownership

- Niwa writes description/focus metadata only to the active supported integration: Rank Math, Yoast, or Niwa fallback meta.
- WordPress/the active SEO integration owns canonical and social tag output; Niwa does not spray competing tags.
- When Yoast or Rank Math owns the schema graph, Niwa does not emit a competing BlogPosting/Breadcrumb graph.
- Otherwise, BlogPosting and Breadcrumb schema use the real stored permalink, post dates, featured image, and a real WordPress author.
- Schema is regenerated after update and after the publish transition.
- Invalid JSON is skipped; the renderer never emits an empty JSON-LD script.

## Render contract

- CSS, JavaScript, and JSON-LD never live in `post_content`.
- Every generated selector is scoped under `.niwa-blog-article`; no global theme thumbnail/header selectors are emitted.
- The progress bar uses a scoped class lookup rather than a reusable fixed ID.
- TOC anchors and headings share the same deduplicated ID records.
- Generated HTML is sanitized with `wp_kses_post` and uses block wrappers which remain valid under normal WordPress content filters.
- Assets render only for the matching singular post, not archive/home pages.
- CSS includes mobile width and overflow guards.

Stored asset keys:

- `_niwa_blog_css`
- `_niwa_blog_progress_js`
- `_niwa_blog_schemas`

Managed-state keys are constants on `Niwa_AI_Blog_Quality_Gate` and cover the marker, policy, source content/format/hash, report, selected links, focus query, intent, audience, money page, language, and supplied evidence URLs.

## Rollback

Update rollback snapshots restore:

- title, content, excerpt, status, slug, author, and dates;
- categories, tags, and thumbnail;
- every Yoast/Rank Math/Niwa SEO and relevant social key captured by the pipeline;
- all three asset keys;
- every managed-state key;
- exact prior key presence and all prior values.

A failed intermediate write returns failure and attempts the exact snapshot restore. New drafts are trashed on rollback. New taxonomy terms are removed only after the restored/deleted post no longer uses them.

The legacy `Niwa_AI_Blog_Migrator` remains an explicit capability/nonce-protected, dry-run-capable repair. Plugin activation does not rewrite historical posts.

## Settings

Truthful feature settings are:

| Key | Meaning |
| --- | --- |
| `auto_toc` | Include the generated TOC |
| `progress_bar` | Include scoped progress markup/JavaScript |
| `json_ld` | Emit Niwa schema only when Niwa owns schema |
| `allow_new_taxonomy_terms` | Permit explicitly requested missing terms during execution; off by default |

The mandatory quality/publish gate has no disable setting. Removed share-button and audit toggles no longer promise runtime output which does not exist.

## Operational workflow

1. Load the bundled `blog-post-design` skill.
2. Read existing content and use `scan_site_for_linking` when link candidates are not already known.
3. Write useful Markdown with H2/H3 hierarchy, evidence-safe claims, real contextual links, controlled taxonomy, and one relevant next step.
4. Call `create_niwa_blog`.
5. If the gate blocks, repair its exact critical items and retry the same canonical path.
6. Return draft ID, preview/edit URLs, selected links, taxonomy, and quality report.
7. After founder approval, preview/call `publish_blog_post` for that exact revision.

No step falls back to a raw low-quality writer.
