Skip to content

Campus Connect: CC status dropdown + MCP vetting server (issue #1714 points 1 & 3)#1720

Closed
dorsvenabili wants to merge 6 commits into
productionfrom
prototype/cc-vetting-rest-api
Closed

Campus Connect: CC status dropdown + MCP vetting server (issue #1714 points 1 & 3)#1720
dorsvenabili wants to merge 6 commits into
productionfrom
prototype/cc-vetting-rest-api

Conversation

@dorsvenabili
Copy link
Copy Markdown
Collaborator

@dorsvenabili dorsvenabili commented May 14, 2026

Summary

Implements points 1 and 3 of #1714.

Point 1 — CC status dropdown (10 statuses, scoped transitions, CC-specific labels)

Restricts the WordCamp Status dropdown, transition map, and status-change log to Campus Connect–specific values for entries where event_subtype equals campusconnect. Non-CC event subtypes (WordCamp, DoAction, Student Club, Other Event) are unaffected.

wcpt-wordcamp/wordcamp-loader.php

  • Added wcpt-needs-action and wcpt-needs-more-info to get_post_statuses(). Both are registered globally so WordPress is aware of them, but excluded from non-CC dropdowns via the WordCamp_Admin override.
  • New get_campus_connect_statuses(): returns the ten CC-specific statuses with CC-specific labels (wcpt-approved-pre-pl → "Approved For Pre-Planning"; wcpt-more-info-reque → "On Hold").
  • New get_campus_connect_status_transitions(): dedicated transition map for CC entries (see table below).

wcpt-wordcamp/wordcamp-admin.php

  • get_post_statuses() override: CC posts → 10 CC statuses; non-CC posts → original list with wcpt-needs-action and wcpt-needs-more-info stripped.
  • get_valid_status_transitions() override: CC posts → CC transition map; non-CC posts → original map, unchanged.
  • New is_campus_connect_post(): reads get_post_meta( $id, 'event_subtype' ) to determine whether the post being edited is a CC entry.
  • New get_status_label() override: returns CC-specific labels for log entries on CC posts; falls back to parent for everything else.
  • require_complete_meta_to_publish_wordcamp(): when scheduling validation fails on a CC post, reverts to wcpt-approved-pre-pl instead of the non-CC wcpt-needs-schedule.
  • enforce_post_status(): blocks CC-exclusive statuses (wcpt-needs-action, wcpt-needs-more-info) from being saved on non-CC posts.

wcpt-event/class-event-admin.php

  • log_status_changes(): replaced direct get_post_status_object()->label calls with a new overridable get_status_label( $status, $post ) method.
  • New get_status_label() base implementation wraps get_post_status_object(); subclasses can override for event-subtype–specific labels. No behaviour change for non-CC event types.

views/wordcamp/metabox-status.php

  • Dropdown options now render $post_status_label (from the get_post_statuses() foreach) instead of $status->label (globally registered), so subtype-specific labels are respected at render time.
  • Injects the current status as a disabled option when it is absent from get_post_statuses() (e.g. a mid-transition state not in the CC set), so saving never silently mutates the status.

Campus Connect transition map

From Can transition to
Needs Vetting Needs Action, On Hold, Needs More Info, Approved For Pre-Planning, Declined, Cancelled
Needs Action Needs Orientation, On Hold, Needs More Info, Approved For Pre-Planning, WordCamp Scheduled, Declined, Cancelled
Needs Orientation On Hold, Needs More Info, Approved For Pre-Planning, WordCamp Scheduled, Declined, Cancelled
Needs More Info Needs Action, On Hold, Approved For Pre-Planning, Declined, Cancelled
On Hold Any CC status
Approved For Pre-Planning On Hold, WordCamp Scheduled, Declined, Cancelled
WordCamp Scheduled On Hold, WordCamp Closed, Declined, Cancelled
WordCamp Closed On Hold, Declined, Cancelled
Declined Any CC status
Cancelled Any CC status

Point 3 — MCP vetting server (wcpt/get-vetting-queue + wcpt/process-application)

Adds the server-side integration surface for the automated vetting agent using the WordPress Abilities API (MCP HTTP transport, WP 6.9+).

wcpt-wordcamp/class-wcpt-vetting-abilities.php (new file)

Registers two WordPress Abilities exposed at /wp-json/mcp/wcpt-vetting:

wcpt/get-vetting-queue
Returns all Campus Connect posts (event_subtype = campusconnect) in wcpt-needs-vetting status, ordered oldest-first. Supports page and per_page parameters for pagination. Each item includes id, title, status, date, content, edit_url, and a meta object with the organizer fields the agent needs to evaluate (Organizer Name, WordPress.org Username, Email Address, Location, Number of Anticipated Attendees).

wcpt/process-application
Accepts post_id (int, required) and note (string, required). Validates the post is a Campus Connect entry in wcpt-needs-vetting, then:

  1. Writes the note as a _note private meta entry (via wcpt_add_private_note()).
  2. Advances the post to wcpt-needs-action via wp_update_post().
  3. Writes a _status_change log entry with CC-specific labels ("Needs Vetting → Needs Action").

Both abilities require the wordcamp_wrangle_wordcamps capability. Auth via WordPress Application Passwords on a dedicated service account.

wcpt-functions.php

New wcpt_add_private_note( $post_id, $message, $user_id = 0 ) helper: low-level counterpart to Event_Admin::validate_and_add_note() that works outside the admin UI (MCP, cron). Available for points 4/5 (email + admin notice) as well.

wcpt-wordcamp/wordcamp-loader.php

Requires the new abilities class and hooks register_vetting_abilities to init.

Notes for reviewers

  • WordCamp_Admin is only loaded when is_admin() is true, so its transition_post_status hook for status-change logging does not fire during MCP requests. The ability handler writes the log entry explicitly using WordCamp_Loader::get_campus_connect_statuses() for correct CC labels.
  • The MCP transport follows the JSON-RPC 2.0 over HTTPS pattern introduced in WP 6.9 (wp_register_ability(), wp_register_ability_category()).
  • The queue runner script that drives the agent will be shared in the coming days (mentioned in Automate vetting and approval workflow for Campus Connect events #1714).

Files changed

File Change
wcpt-wordcamp/wordcamp-loader.php CC status methods; register abilities class + hook
wcpt-wordcamp/wordcamp-admin.php CC dropdown scoping; is_campus_connect_post(); CC label override; enforce/schedule CC fallbacks
wcpt-event/class-event-admin.php Overridable get_status_label() + use in log_status_changes()
views/wordcamp/metabox-status.php Render CC labels; inject missing-status guard
wcpt-wordcamp/class-wcpt-vetting-abilities.php New — MCP ability handlers (queue + process)
wcpt-functions.php New wcpt_add_private_note() helper

Test plan

CC status dropdown (Point 1)

  • Open a CC post (event_subtype = campusconnect). Confirm the status dropdown shows exactly the 10 CC statuses and no others.
  • Confirm "Approved For Pre-Planning" appears as the label for wcpt-approved-pre-pl in the CC dropdown. Confirm non-CC posts still show "Approved for Pre-Planning Pending Agreement" for the same slug.
  • Confirm "On Hold" appears as the label for wcpt-more-info-reque in the CC dropdown. Confirm non-CC posts show the original label.
  • On a CC post in Needs Vetting, confirm only the expected next statuses are enabled (Needs Action, On Hold, Needs More Info, Approved For Pre-Planning, Declined, Cancelled). Confirm all others are disabled.
  • Transition a CC post through several statuses. Confirm the status-change log entries use CC-specific labels.
  • On a CC post in Approved For Pre-Planning, attempt to save with status WordCamp Scheduled while leaving required fields empty. Confirm the post remains in Approved For Pre-Planning and the validation error is shown.
  • Open a non-CC post (WordCamp, DoAction, etc.). Confirm the full original status list is shown, wcpt-needs-action and wcpt-needs-more-info are absent, and transition enforcement is unchanged.
  • On a CC post in WordCamp Closed, confirm only On Hold, Declined, and Cancelled are enabled in the status dropdown and all other statuses are disabled.
  • Attempt to save a non-CC post with post_status = wcpt-needs-action via direct POST. Confirm enforce_post_status() rejects it and the status is unchanged.

MCP vetting server (Point 3)

  • GET /wp-json/mcp/wcpt-vetting (initialize) unauthenticated → 401
  • MCP wcpt/get-vetting-queue with non-wrangler user → error / 403
  • MCP wcpt/get-vetting-queue as wrangler, no CC posts in needs-vetting → empty items array
  • MCP wcpt/get-vetting-queue with a CC post in wcpt-needs-vetting → item appears with correct fields
  • MCP wcpt/get-vetting-queue with a non-CC post in wcpt-needs-vetting → does not appear
  • MCP wcpt/process-application with unknown post_id → error
  • MCP wcpt/process-application on a non-CC post → error
  • MCP wcpt/process-application on a CC post not in wcpt-needs-vetting → error
  • MCP wcpt/process-application missing note → error
  • MCP wcpt/process-application on a valid CC post in wcpt-needs-vetting → success; post status is wcpt-needs-action; _note meta written; _status_change log entry reads "Needs Vetting → Needs Action"
  • After a successful process, the post no longer appears in wcpt/get-vetting-queue

Generated with Claude Code

…point 3)

Introduces two authenticated REST endpoints under `wordcamp/v1/vetting/`:

- `GET /queue` — returns Campus Connect posts in `wcpt-needs-vetting` status,
  with organizer meta fields the vetting agent needs to evaluate each application.
- `POST /process` — accepts a post ID and vetting note; writes the note as a
  private `_note` meta entry and advances the post to `wcpt-needs-action`.

Both endpoints require the `wordcamp_wrangle_wordcamps` capability (Application
Passwords is the intended auth mechanism for the automated agent).

Also adds `wcpt_add_private_note()` to `wcpt-functions.php` as a reusable helper
for programmatically writing private notes outside the admin UI (used here and
available for future use in Points 4/5).

Status-change log entries are written explicitly by the controller since
`WordCamp_Admin` (which normally hooks `transition_post_status`) is not loaded
outside the admin context. CC-specific labels from
`WordCamp_Loader::get_campus_connect_statuses()` are used so log entries read
"Needs Vetting -> Needs Action" rather than raw slugs.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Copilot AI review requested due to automatic review settings May 14, 2026 10:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an authenticated REST API surface for an automated Campus Connect “vetting agent” to read the vetting queue, write a private note, and advance an application’s status, plus a small helper for writing private notes programmatically.

Changes:

  • Registers new REST routes under wordcamp/v1/vetting (queue + process) and loads the controller.
  • Implements WordCamp_REST_Vetting_Controller with GET /queue and POST /process.
  • Adds wcpt_add_private_note() helper to write _note private log entries outside the admin UI.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-loader.php Loads the new REST controller and registers routes on rest_api_init.
public_html/wp-content/plugins/wcpt/wcpt-wordcamp/class-wp-rest-vetting-controller.php Implements the vetting queue/process endpoints and status-change logging.
public_html/wp-content/plugins/wcpt/wcpt-functions.php Adds a helper to create _note private log entries programmatically.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +73 to +78
public function check_permission() {
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
__( 'You must be logged in to use the vetting API.', 'wordcamporg' ),
array( 'status' => 401 )
Comment on lines +235 to +240
$result = wp_update_post(
array(
'ID' => $post_id,
'post_status' => 'wcpt-needs-action',
),
true
Comment on lines +272 to +276
$locale_switched = switch_to_locale( 'en_US' );

$cc_statuses = WordCamp_Loader::get_campus_connect_statuses();
$old_label = $cc_statuses[ $old_status ] ?? $old_status;
$new_label = $cc_statuses[ $new_status ] ?? $new_status;
Comment on lines +104 to +110
'post_type' => WCPT_POST_TYPE_ID,
'post_status' => 'wcpt-needs-vetting',
'meta_key' => 'event_subtype',
'meta_value' => 'campusconnect',
'posts_per_page' => 100,
'orderby' => 'date',
'order' => 'ASC',
Comment on lines +229 to +235
// 1. Attach the vetting note.
wcpt_add_private_note( $post_id, $note, get_current_user_id() );

// 2. Advance the status.
$old_status = $post->post_status;

$result = wp_update_post(
Comment on lines +135 to +139
public function prepare_item_for_response( $post, $request ) {
$meta_fields = array(
'Organizer Name',
'WordPress.org Username',
'Email Address',
dorsvenabili and others added 2 commits May 14, 2026 14:33
- Convert indented two-line inline comment to block comment in
  process_application() (Squiz.Commenting.InlineComment.SpacingBefore)
- Remove trailing spaces before array closer in register_vetting_rest_routes
  hook registration (WordPress.Arrays.ArrayDeclaration)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Add WP_REST_Request $request parameter to check_permission() to match
  the WP_REST_Controller interface signature
- Add pagination to GET /vetting/queue: page/per_page args (default 1/20,
  max 100), X-WP-Total and X-WP-TotalPages response headers via WP_Query
- Validate that the vetting note is non-empty before any writes (400)
- Write the note before advancing the status, and check the return value
  of wcpt_add_private_note(); abort with 500 if it fails
- Re-fetch the post after writing the note to detect concurrent status
  changes (optimistic concurrency); return 409 if status shifted
- Rename prepare_item_for_response() to prepare_queue_item() to avoid
  accidentally overriding the WP_REST_Controller base-class method

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Copilot AI review requested due to automatic review settings May 18, 2026 14:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment on lines +337 to +341
$locale_switched = switch_to_locale( 'en_US' );

$cc_statuses = WordCamp_Loader::get_campus_connect_statuses();
$old_label = $cc_statuses[ $old_status ] ?? $old_status;
$new_label = $cc_statuses[ $new_status ] ?? $new_status;
Comment on lines +273 to +293
// 1. Attach the vetting note first so it is always paired with the transition.
$note_id = wcpt_add_private_note( $post_id, $note, get_current_user_id() );

if ( ! $note_id ) {
return new WP_Error(
'wcpt_vetting_note_failed',
__( 'Could not save the vetting note. Status was not changed.', 'wordcamporg' ),
array( 'status' => 500 )
);
}

// 2. Reload the post to guard against a concurrent status change (optimistic concurrency check).
$post = get_post( $post_id );

if ( ! $post || 'wcpt-needs-vetting' !== $post->post_status ) {
return new WP_Error(
'wcpt_vetting_concurrent_update',
__( 'The application status changed while this request was being processed. Please reload and try again.', 'wordcamporg' ),
array( 'status' => 409 )
);
}
dorsvenabili and others added 2 commits May 20, 2026 16:08
… (issue #1714)

- Delete class-wp-rest-vetting-controller.php integration; keep file for PR #1720 reference
- Add class-wcpt-vetting-abilities.php: registers wcpt/get-vetting-queue and
  wcpt/process-application as WordPress Abilities (WP 6.9+ Abilities API)
- Expose both abilities via a dedicated MCP server at /wp-json/mcp/wcpt-vetting
  using the wordpress/mcp-adapter Composer package (v0.5.0)
- Add wcpt/vendor/ with plugin-scoped Composer install of mcp-adapter
- Update wordcamp-loader.php: bootstrap McpAdapter directly from vendor autoload,
  register the wcpt ability category, call WCPT_Vetting_Abilities::init()
- Add WordCamp_Loader::get_campus_connect_statuses() for log label lookups

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…p (issue #1714 point 1)

- Expand get_campus_connect_statuses() from 7 to 10 statuses: add Needs Orientation
  (wcpt-needs-orientati), WordCamp Scheduled, and WordCamp Closed
- Register wcpt-needs-action and wcpt-needs-more-info in get_post_statuses() so
  Event_Loader::register_post_statuses() calls register_post_status() for them
- Add WordCamp_Loader::get_campus_connect_status_transitions() with the definitive
  transition map; Declined, Cancelled and On Hold can transition to any CC status
- WordCamp_Admin::get_post_statuses() returns CC statuses for CC posts, strips
  CC-exclusive slugs for all other subtypes
- WordCamp_Admin::get_valid_status_transitions() delegates to the CC map for CC posts
- Add WordCamp_Admin::is_campus_connect_post() (reads event_subtype meta)
- Add WordCamp_Admin::get_status_label() override for CC-specific log labels
- Add Event_Admin::get_status_label() base method; log_status_changes() calls it
- metabox-status.php: use labels from get_post_statuses() not the global WP status
  object; inject current status as disabled option when absent from the list
- enforce_post_status() blocks CC-exclusive statuses on non-CC posts
- Scheduled validation fallback reverts CC posts to wcpt-approved-pre-pl

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@dorsvenabili dorsvenabili changed the title Campus Connect: REST API vetting endpoints (issue #1714 point 3) Campus Connect: CC status dropdown + MCP vetting server (issue #1714 points 1 & 3) May 21, 2026
- class-event-admin.php: add missing docblock for log_status_changes()
- class-wcpt-vetting-abilities.php:
  - Inline comments now end with full-stops (resources, prompts)
  - @throws tag corrected to \RuntimeException (fully-qualified)
  - All __() calls inside throw statements replaced with esc_html__()
  - Dynamic values in exception messages wrapped with esc_html()

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Copilot AI review requested due to automatic review settings May 21, 2026 13:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@dorsvenabili
Copy link
Copy Markdown
Collaborator Author

Superseded by #1727. The head branch has been renamed from prototype/cc-vetting-rest-api to prototype/wp7-mcp-vetting (GitHub does not allow changing a PR's head branch in place). All commits and the PR description are identical — closing this in favour of #1727.

@dorsvenabili dorsvenabili deleted the prototype/cc-vetting-rest-api branch May 21, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants