feat: PRAB article JSON-LD emitter + meta reader (v0.8.0)

- PR_Core_Jsonld_Article: meta-triggered emitter for PRAutoBlogger posts
  (trigger: _prab_schema_version=1 on post type). Yoast path retypes page
  node to MedicalWebPage (wpseo_schema_webpage_type priority 10) and enriches
  Article piece with citation[], about[]→Drug @id, lastReviewed, honest
  reviewedBy (wpseo_schema_graph priority 13). No-Yoast fallback emits
  standalone @graph. Posts without trigger meta are byte-unchanged.

- PR_Core_Prab_Meta_Reader: sanitises all _prab_* contract-v1 meta at read
  time. URLs validated (http/https), DOIs normalised to https://doi.org/ URI,
  JSON arrays per-element validated, WP user ID resolved. Malformed values
  degrade gracefully. Honest reviewedBy: Person ONLY when mode=human + valid
  user ID; else Organization. Never a fabricated person.

- Unit tests: JsonldArticleTest.php — 22 assertions covering is_triggered,
  review_mode, reviewed_by Person/Org decision tree, citation sanitization,
  DOI normalisation, about linkage, retype passthrough, enrich, no-new-nodes,
  editorial-system-never-person.

- bootstrap.php: WP_User stub + get_userdata reads pr_test_userdata global.
- ARCHITECTURE.md: updated JSON-LD class tree + PRAB emission section.
- CONVENTIONS.md: PRAB contract v2 extension guide added.
- CHANGELOG.md + version bump to v0.8.0.

Ratified contract: convo/prcore/decisions/2026-06-11-jsonld-contract-v1.md
File sizes: article.php=235L, meta-reader.php=265L (both < 300-line gate).
No duplicate Article/WebPage nodes. uninstall.php unaffected (_prab_* unowned).

Agent-Session: prcore-jsonld-article-20260622
This commit is contained in:
peptiderepo
2026-06-23 04:23:21 +00:00
parent a7a2bb2f6d
commit 1ce471981b
9 changed files with 950 additions and 4 deletions

View File

@@ -61,6 +61,7 @@ $GLOBALS['pr_core_options'] = [];
$GLOBALS['pr_core_cron_calls'] = [];
$GLOBALS['pr_test_postmeta'] = [];
$GLOBALS['pr_test_posts'] = [];
$GLOBALS['pr_test_userdata'] = [];
$GLOBALS['pr_test_updated_meta'] = [];
$GLOBALS['pr_test_is_singular'] = false;
$GLOBALS['pr_test_singular_type'] = '';
@@ -325,9 +326,28 @@ if ( ! function_exists( 'get_post_modified_time' ) ) {
}
if ( ! function_exists( 'get_userdata' ) ) {
function get_userdata( int $id ) {
$data = $GLOBALS['pr_test_userdata'][ $id ] ?? null;
if ( null !== $data ) {
return $data instanceof WP_User ? $data : new WP_User( (array) $data );
}
return false;
}
}
if ( ! class_exists( 'WP_User' ) ) {
class WP_User {
public int $ID = 0;
public string $display_name = '';
public string $user_login = '';
public string $user_email = '';
/** @param array<string, mixed> $data */
public function __construct( array $data = [] ) {
foreach ( $data as $key => $value ) {
$this->$key = $value;
}
}
}
}
if ( ! function_exists( 'get_author_posts_url' ) ) {
function get_author_posts_url( int $id ): string {
return 'https://peptiderepo.com/author/' . $id;