Bootstrap: includes/class-bootstrap.php. Feature modules live under includes/ and admin React apps under includes/admin/.
Modules
| Module | Role |
|---|---|
taxonomy/class-workflow-status.php | Private workflow-status taxonomy; seeds terms; fires prc_publish_workflows_workflow_status_changed |
watchers/class-watchers.php | prc_watchers meta, watchers + watchChildPosts REST fields, POST …/watch-self |
notifications/class-notifications.php | Async Slack-only DMs via Action Scheduler |
workspaces/ | workspace CPT, REST, tracker panels, MCP search abilities (search-workspaces, get-workspace) |
checklist/ | Publication checklist engine, first-party checks, editor plugin |
admin/ | Status Board, Calendar, Workspaces, dashboard widget, editor SlotFills |
user-profile/class-slack-user-id.php | prc_slack_user_id profile field and Connect Slack button |
Workflow status
- Taxonomy is private, REST-exposed at
/wp/v2/workflow-status, hidden from the default taxonomy meta box. - Registration runs at
init11 fromWatchers::get_post_types(). Types that opt in later (email campaigns atinit20) are attached atinit99 so REST can persist the term. - Editor UI uses
PluginPostStatusInfoand writeseditPost({ 'workflow-status': [termId] }). - Becoming
publishclears the term (Status Board, Gutenberg REST, WP-CLI, andfuture→publish).rest_pre_insert_{post_type}empties the REST assignment so Gutenberg cannot write the leftover term back afterwp_insert_post. Clearing that leftover term does not notify as a move to draft; the publish hop keeps the prior stage for watchers. - Final draft → publish also rides
prc_platform_async_on_publish/prc_platform_async_on_unpublish.
Default post type for taxonomy + watchers is post, filterable with prc_publish_workflows_post_types. Other CPTs opt in with add_post_type_support( $type, 'prc-publish-workflows' ).
Watch child posts
Posts with multiSectionReport meta (report packages) can set prc_watch_child_posts. The editor writes this through the watchChildPosts REST field (registered before watchers so combined saves persist the toggle first). When enabled, Watchers::mirror_parent_watchers_to_children() adds or removes only the current user on each chapter postId — not the full parent watcher list. Auto-add via POST …/watch-self and chapter-meta changes use the same mirror path. Toggle-off skips child writes on that save; it does not retroactively clear chapter watchers.
Workspaces
- CPT
workspace: non-public UI,show_in_rest, supports title, excerpt, author, custom fields, andprc-publish-workflows(so watchers meta applies). - Items meta
prc_workspace_items:{ id, type, objectId, url, label, addedBy, addedAt }withtypeofpost,attachment, orlink. Not exposed via core REST; all writes go through the custom controller. - Tracker project refs
prc_workspace_linear_projectandprc_workspace_asana_project:{ id, name, url }or empty. Not REST-exposed.Workspace_Trackersowns persist/get.Linear_Tracker(GraphQL) andAsana_Tracker(REST) fetch live lists (max 50). Routes live inWorkspaces_Trackers_REST_Controller. - Uploaded media store
_prc_workspace_idfor traceability. - MCP abilities
prc-publish-workflows/search-workspacesandprc-publish-workflows/get-workspacesearch title, description, and collected items (labels, filenames, URLs). RESTGET /workspaces?search=stays title/excerpt-only.
Build bundles
| Bundle | Consumer |
|---|---|
build/watchers-panel/ | Watchers sidebar |
build/post-status-info/ | Workflow badge in Status & visibility |
build/status-board/ | Status Board admin |
build/calendar/ | Calendar admin |
build/dashboard-widget/ | Posts in production widget |
build/checklist/ | Publication checklist panel + sidebar |
build/workspaces/ | Workspaces DataViews app |
Hooks (summary)
| Hook | Kind | Purpose |
|---|---|---|
prc_publish_workflows_post_types | Filter | Post types for taxonomy + watchers |
prc_publish_workflows_status_pipeline | Filter | Status Board column definitions |
prc_publish_workflows_workflow_status_changed | Action | Stage change |
prc_publish_workflows_should_notify | Filter | Suppress notifications |
prc_publish_workflows_register_checks | Action | Register checks with register_check() |
prc_publish_workflows_block_on_failing | Filter | Block publish when a check is incomplete |
prc_publish_workflows_checklist_enabled_types | Filter | Post types for the prc_checklist REST field |
prc_publish_workflows_publish_authorizer_roles | Filter | Roles that may publish any type |
prc_publish_workflows_editor_may_publish_types | Filter | Types the Editor role may also publish. Default short-read, prc_email_campaign, prc_email_txn |
prc_publish_workflows_notification_slack_message | Filter | Slack DM body |
Publication checklist
The engine lives in includes/checklist/. Publication_Checklist holds a registry of checks. Sibling plugins and blocks call PRC\Platform\Publish_Workflows\register_check() on prc_publish_workflows_register_checks (fires on plugins_loaded). Each check returns a Status (complete, incomplete, or info).
Results are exposed on enabled post types as the readonly REST field prc_checklist. The editor plugin (build/checklist/) reads that field and renders a pre-publish panel plus a sidebar. Replace one row with addFilter( 'prc-publish-workflows.checklist.item.{check_id}', ... ).
prc_publish_workflows_block_on_failing is off by default. When it is on, incomplete checks lock the publish button and rest_pre_insert_{type} / wp_insert_post_data refuse a change to a published status.
Publish permission is a separate always-on gate. Administrators and producers can confirm Ready to publish in the pre-publish panel. Editors can do that for short-read, prc_email_campaign, and prc_email_txn. Designers can do that for chart. Other roles cannot toggle the control. REST returns prc_publish_not_authorized (403) if they try to publish anyway. Already-published updates and cron go-lives are not blocked.