Skip to content

Commit 360ea77

Browse files
authored
Fix factual errors in Claude skills and agents (#3370)
1 parent c2e8c25 commit 360ea77

8 files changed

Lines changed: 123 additions & 101 deletions

File tree

.agents/skills/code-style/SKILL.md

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class Custom extends Base {
8787
**Examples:**
8888
- `includes/transformer/class-post.php` - Post transformation.
8989
- `includes/transformer/class-comment.php` - Comment transformation.
90-
- `includes/transformer/class-event.php` - Event-specific transformation.
90+
- `includes/transformer/class-user.php` - User/actor transformation.
9191

9292
### Handlers
9393
Process incoming ActivityPub activities from remote servers.
@@ -137,11 +137,8 @@ $content = enrich_content_data( $content, $pattern, $callback );
137137
// Resolve WebFinger handle to actor URL.
138138
$resource = Webfinger::resolve( $handle );
139139

140-
// Get user's ActivityPub actor URL.
141-
$actor_url = get_author_posts_url( $user_id );
142-
143-
// Check if a post type is enabled for ActivityPub.
144-
$enabled = \is_post_type_enabled( $post_type );
140+
// Check whether a post is disabled for ActivityPub (the federation pipeline gate).
141+
$disabled = is_post_disabled( $post );
145142
```
146143

147144
## Real Codebase Examples
@@ -153,14 +150,14 @@ $enabled = \is_post_type_enabled( $post_type );
153150
- `includes/class-signature.php` - HTTP Signatures for federation.
154151

155152
**Activity Types:**
156-
- `includes/activity/class-activity.php` - Base activity class.
157-
- `includes/activity/class-follow.php` - Follow activity.
158-
- `includes/activity/class-undo.php` - Undo activity.
153+
- `includes/activity/class-activity.php` - Activity class (Create, Follow, Undo, etc. are built from this).
154+
- `includes/activity/class-base-object.php` - Base object class.
155+
- `includes/activity/extended-object/` - Extended object types (e.g. Event).
159156

160157
**Integrations (see [Integration Patterns](../integrations/SKILL.md)):**
161-
- `integration/class-woocommerce.php` - WooCommerce integration.
162158
- `integration/class-buddypress.php` - BuddyPress integration.
163159
- `integration/class-jetpack.php` - Jetpack integration.
160+
- `integration/class-opengraph.php` - OpenGraph integration.
164161

165162
## Common Initialization Patterns
166163

@@ -172,7 +169,7 @@ class Feature {
172169
*/
173170
public static function init() {
174171
\add_action( 'init', array( self::class, 'register' ) );
175-
\add_filter( 'activitypub_activity_object', array( self::class, 'filter' ) );
172+
\add_filter( 'activitypub_the_content', array( self::class, 'filter' ) );
176173
}
177174
}
178175
```
@@ -199,16 +196,15 @@ class Manager {
199196

200197
**Actions:**
201198
```php
202-
\do_action( 'activitypub_before_send_activity', $activity );
203-
\do_action( 'activitypub_after_send_activity', $activity, $response );
204-
\do_action( 'activitypub_inbox_received', $activity );
199+
\do_action( 'activitypub_handled_create', $activity, $user_ids, $success, $result );
200+
\do_action( 'activitypub_followers_pre_remove_follower', $follower, $user_id, $actor );
205201
```
206202

207203
**Filters:**
208204
```php
209-
$activity = \apply_filters( 'activitypub_activity_object', $activity, $post );
205+
$array = \apply_filters( 'activitypub_activity_object_array', $array, $class, $id, $object );
210206
$content = \apply_filters( 'activitypub_the_content', $content, $post );
211-
$actor = \apply_filters( 'activitypub_actor_data', $actor, $user_id );
207+
$types = \apply_filters( 'activitypub_actor_types', $types );
212208
```
213209

214210
## Version Numbers

.agents/skills/integrations/SKILL.md

Lines changed: 87 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,32 @@ All integrations live in the `integration/` directory.
1414

1515
**File naming:** `class-{plugin-name}.php` (following PHP conventions in AGENTS.md)
1616

17-
### Available Integrations
18-
- BuddyPress
19-
- bbPress
20-
- WooCommerce
21-
- Jetpack
22-
- The Events Calendar
23-
- WP User Avatars
24-
- And 13+ more
17+
**Namespace:** `Activitypub\Integration`
18+
19+
### How Integrations Load
20+
Integrations are wired up in `integration/load.php` inside `plugin_init()`. Each one is guarded by a detection check and then calls its class's static `init()`:
21+
22+
```php
23+
// integration/load.php
24+
if ( \defined( 'JETPACK__VERSION' ) ) {
25+
Jetpack::init();
26+
}
27+
```
28+
29+
### Existing Integrations
30+
The real integrations shipped today (see `integration/`):
31+
32+
`akismet`, `buddypress`, `classic-editor`, `enable-mastodon-apps`, `jetpack`,
33+
`litespeed-cache`, `multisite-language-switcher`, `nodeinfo`, `opengraph`,
34+
`podlove-podcast-publisher`, `seriously-simple-podcasting`, `surge`, `webfinger`,
35+
`wp-rest-cache`, `wpml`, `yoast-seo`.
36+
37+
A few examples:
38+
- **Akismet** — runs inbound comments/interactions through Akismet spam checks.
39+
- **OpenGraph** — adds `fediverse:creator` and related metadata via the OpenGraph plugin.
40+
- **Enable Mastodon Apps** — lets Mastodon client apps talk to the site.
41+
- **Caches (LiteSpeed Cache, Surge, WP REST Cache)** — keep ActivityPub responses cacheable/uncached correctly.
42+
- **Multilingual (WPML, Multisite Language Switcher)** — language-aware actor and content handling.
2543

2644
For complete directory structure and naming conventions, see `docs/php-class-structure.md`.
2745

@@ -34,58 +52,80 @@ For complete directory structure and naming conventions, see `docs/php-class-str
3452
namespace Activitypub\Integration;
3553

3654
class Plugin_Name {
55+
/**
56+
* Initialize the class, registering WordPress hooks.
57+
*/
3758
public static function init() {
38-
\add_filter( 'activitypub_transformer', array( self::class, 'custom_transformer' ), 10, 2 );
39-
\add_filter( 'activitypub_post_types', array( self::class, 'add_post_types' ) );
59+
// Enable federation for the plugin's post type.
60+
\add_post_type_support( 'plugin_post_type', 'activitypub' );
61+
62+
// Provide a custom transformer if the default Post transformer is not enough.
63+
\add_filter( 'activitypub_transformer', array( self::class, 'transformer' ), 10, 3 );
4064
}
4165

42-
public static function custom_transformer( $transformer, $object ) {
43-
// Return custom transformer if needed.
66+
/**
67+
* Return a custom transformer for the plugin's objects.
68+
*
69+
* @param mixed $transformer The transformer to use. Default null.
70+
* @param mixed $data The object to transform.
71+
* @param string $object_class The class of the object to transform.
72+
* @return mixed
73+
*/
74+
public static function transformer( $transformer, $data, $object_class ) {
75+
if ( 'WP_Post' === $object_class && 'plugin_post_type' === $data->post_type ) {
76+
require_once __DIR__ . '/class-custom-transformer.php';
77+
return new Custom_Transformer( $data );
78+
}
4479
return $transformer;
4580
}
81+
}
82+
```
4683

47-
public static function add_post_types( $post_types ) {
48-
// Add plugin's post types.
49-
$post_types[] = 'plugin_post_type';
50-
return $post_types;
51-
}
84+
Then register it in `integration/load.php`:
85+
86+
```php
87+
if ( \defined( 'PLUGIN_VERSION' ) ) {
88+
Plugin_Name::init();
5289
}
5390
```
5491

5592
## Integration Patterns
5693

57-
### Adding Post Type Support
94+
### Enabling Federation for a Post Type
95+
96+
Post-type support drives which content federates. Use `add_post_type_support()` — there is no `activitypub_post_types` filter:
5897

5998
```php
60-
public static function add_post_types( $post_types ) {
61-
$post_types[] = 'event';
62-
$post_types[] = 'product';
63-
return $post_types;
64-
}
99+
\add_post_type_support( 'event', 'activitypub' );
100+
\add_post_type_support( 'product', 'activitypub' );
65101
```
66102

103+
The enabled post types are stored in the `activitypub_support_post_types` option (read with `\get_option( 'activitypub_support_post_types', array( 'post' ) )`).
104+
67105
### Custom Transformers
68106

107+
The `activitypub_transformer` filter takes **three** arguments — `( $transformer, $data, $object_class )` — and is registered with priority `10, 3`:
108+
69109
```php
70-
public static function transformer( $transformer, $object ) {
71-
if ( 'custom_type' === get_post_type( $object ) ) {
72-
require_once __DIR__ . '/transformer/class-custom.php';
73-
return new Transformer\Custom( $object );
110+
\add_filter( 'activitypub_transformer', function ( $transformer, $data, $object_class ) {
111+
if ( 'WP_Post' === $object_class && 'event' === $data->post_type ) {
112+
return new Event_Transformer( $data );
74113
}
75114
return $transformer;
76-
}
115+
}, 10, 3 );
77116
```
78117

79-
### Modifying Activities
118+
### Modifying the Activity Object
119+
120+
To tweak the final ActivityStreams array, hook `activitypub_activity_object_array``( $array, $class, $id, $object )`:
80121

81122
```php
82-
\add_filter( 'activitypub_activity_object', function( $object, $post ) {
83-
if ( 'product' === get_post_type( $post ) ) {
84-
$object['type'] = 'Product';
85-
$object['price'] = get_post_meta( $post->ID, 'price', true );
123+
\add_filter( 'activitypub_activity_object_array', function ( $array, $class, $id, $object ) {
124+
if ( isset( $array['type'] ) && 'Note' === $array['type'] ) {
125+
// Adjust the outgoing object array here.
86126
}
87-
return $object;
88-
}, 10, 2 );
127+
return $array;
128+
}, 10, 4 );
89129
```
90130

91131
## Testing Integrations
@@ -94,7 +134,7 @@ public static function transformer( $transformer, $object ) {
94134

95135
```php
96136
// Check if integration is active.
97-
if ( class_exists( '\Activitypub\Integration\Plugin_Name' ) ) {
137+
if ( \class_exists( '\Activitypub\Integration\Plugin_Name' ) ) {
98138
// Integration loaded.
99139
}
100140
```
@@ -104,23 +144,23 @@ if ( class_exists( '\Activitypub\Integration\Plugin_Name' ) ) {
104144
1. Install target plugin
105145
2. Activate ActivityPub
106146
3. Check for conflicts
107-
4. Verify custom post types work
147+
4. Verify custom post types federate
108148
5. Test federation of plugin content
109149

110150
## Common Integration Issues
111151

112152
### Plugin Detection
113153
```php
114-
// Multiple detection methods.
115-
if ( defined( 'PLUGIN_VERSION' ) ) { }
116-
if ( function_exists( 'plugin_function' ) ) { }
117-
if ( class_exists( 'Plugin_Class' ) ) { }
154+
// Multiple detection methods (match how integration/load.php guards each one).
155+
if ( \defined( 'PLUGIN_VERSION' ) ) { }
156+
if ( \function_exists( 'plugin_function' ) ) { }
157+
if ( \class_exists( 'Plugin_Class' ) ) { }
118158
```
119159

120160
### Hook Priority
121161
```php
122162
// Use appropriate priority.
123-
add_filter( 'hook', 'callback', 20 ); // After plugin's filter.
163+
\add_filter( 'hook', 'callback', 20 ); // After plugin's filter.
124164
```
125165

126166
### Namespace Conflicts
@@ -129,27 +169,10 @@ add_filter( 'hook', 'callback', 20 ); // After plugin's filter.
129169
$object = new \Plugin\Namespace\Class();
130170
```
131171

132-
## Existing Integrations
133-
134-
### BuddyPress
135-
- Adds BuddyPress activity support
136-
- Custom member transformers
137-
- Group activity federation
138-
139-
### WooCommerce
140-
- Product post type support
141-
- Order activity notifications
142-
- Customer review federation
143-
144-
### bbPress
145-
- Forum topic federation
146-
- Reply activities
147-
- User forum profiles
148-
149172
## Best Practices
150173

151-
1. **Always check if plugin is active** before adding hooks
152-
2. **Use late priority** for filters to override plugin defaults
153-
3. **Test with multiple plugin versions**
154-
4. **Document compatibility requirements**
155-
5. **Handle plugin deactivation gracefully**
174+
1. **Always guard the integration** with a detection check in `integration/load.php` before calling `init()`.
175+
2. **Use late priority** for filters when you need to override another plugin's defaults.
176+
3. **Test with multiple plugin versions.**
177+
4. **Document compatibility requirements.**
178+
5. **Handle plugin deactivation gracefully.**

.agents/skills/pr/SKILL.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ git push --force-with-lease
8686

8787
## Special Cases
8888

89-
**Hotfixes:** Branch `fix/critical-issue`, minimal changes, add "Hotfix" label, request expedited review.
89+
**Hotfixes:** Branch `fix/critical-issue`, minimal changes, add a `[Pri] High` (or `[Pri] BLOCKER`) label, request expedited review.
9090

9191
**Experimental:** Use `try/` prefix, mark as draft, get early feedback, convert to proper branch type once confirmed.
9292

@@ -98,12 +98,12 @@ git push --force-with-lease
9898
|-------|-----|
9999
| `Bug` | Bug fixes |
100100
| `Enhancement` | New features |
101-
| `Documentation` | Doc updates |
101+
| `Docs` | Documentation updates |
102102
| `Code Quality` | Refactoring, cleanup, etc. |
103103
| `Skip Changelog` | No changelog needed |
104-
| `Needs Review` | Ready for review |
105-
| `In Progress` | Still working |
106-
| `Hotfix` | Urgent fix |
104+
| `[Status] Needs review` | Ready for review |
105+
| `[Status] In Progress` | Still working |
106+
| `[Pri] High` / `[Pri] BLOCKER` | Urgent / expedited |
107107

108108
## Reference
109109

.agents/skills/release/SKILL.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ npm run release # Create major/minor release PR.
1515
```
1616

1717
### Version File Locations
18-
When updating versions manually, change these files:
19-
- `activitypub.php` - Plugin header (`Version: X.Y.Z`).
18+
When updating versions manually, change these files (these are the files `bin/release.js` touches):
19+
- `activitypub.php` - Plugin header (`Version: X.Y.Z`) **and** the `ACTIVITYPUB_PLUGIN_VERSION` constant. Both must change.
2020
- `readme.txt` - WordPress.org readme (`Stable tag: X.Y.Z`).
21-
- `package.json` - npm version (`"version": "X.Y.Z"`).
21+
- `includes/class-migration.php` - Version references in the DB-migration `version_compare()` checks.
2222
- `CHANGELOG.md` - Changelog file (auto-updated by release script).
2323

24+
Note: `package.json` has no `version` field and is not part of the release bump.
25+
2426
## Comprehensive Release Guide
2527

2628
See [Release Process](docs/release-process.md) for complete release workflow and detailed steps.

.claude/agents/bug-bounty.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,23 @@ You are a bug-fix agent for the WordPress ActivityPub plugin. Your job is to pic
1010

1111
## Step 1 — Find an Issue to Fix
1212

13-
Fetch open bug issues from the repository. **Only consider issues labeled "Bug" or "[Type] Bug"** — skip everything else:
13+
Fetch open bug issues from the repository. **Only consider issues labeled "Bug"** — skip everything else:
1414

1515
```bash
1616
gh issue list --label "Bug" --state open --json number,title,body,labels --limit 20
17-
gh issue list --label "[Type] Bug" --state open --json number,title,body,labels --limit 20
1817
```
1918

20-
Merge and deduplicate the results.
21-
2219
From the results, **skip issues that also have the "Needs triage" label** — these are unverified and waiting for manual review.
2320

2421
Review the remaining issues and pick the one that is most straightforward to fix — look for clear reproduction steps, well-described expected behavior, and a scope that can be addressed with minimal changes.
2522

26-
**Skip issues that already have a linked PR.** For each candidate issue, check:
23+
**Skip issues that already have a linked PR.** For each candidate issue, check for referencing PRs:
2724

2825
```bash
29-
gh pr list --state open --search "#<number>" --json number,title
26+
gh search prs --repo Automattic/wordpress-activitypub --state open "Fixes #<number> OR Closes #<number> OR #<number>"
3027
```
3128

32-
If any open PR already references the issue, move on to the next candidate.
29+
You can also see linked PRs directly in `gh issue view <number>` (the development/timeline section). If any open PR already references the issue, move on to the next candidate.
3330

3431
## Step 2 — Analyze the Chosen Issue
3532

.claude/agents/patch-release.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ git log --oneline -3
9595

9696
Follow the **release** skill's patch release process:
9797

98-
1. Run `composer changelog:write`use the patch version when prompted.
98+
1. Run `composer changelog:write -- --use-version=<patch-version> --no-interaction` to force the patch version non-interactively (it otherwise derives the version from change-file significance and may prompt).
9999
2. Copy new entries from `CHANGELOG.md` into the `== Changelog ==` section of `readme.txt`.
100-
3. Update version numbers in all version file locations (per the release skill).
100+
3. Update version numbers in all version file locations (per the release skill). In `activitypub.php` this means **both** the `Version:` header **and** the `ACTIVITYPUB_PLUGIN_VERSION` constant, plus `includes/class-migration.php`.
101101
4. Replace any `unreleased` annotations in cherry-picked files with the patch version.
102102

103103
## Step 6 — Review Changes

.claude/agents/support.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ curl -sI -o /dev/null -w "%{http_code}" "SITE_URL/?author=1"
9090
# Check with Accept header for ActivityPub
9191
curl -s -H "Accept: application/activity+json" "SITE_URL/?author=1" | python3 -m json.tool
9292

93-
# Also try the REST API users endpoint
94-
curl -s "SITE_URL/wp-json/activitypub/1.0/actors" | python3 -m json.tool
93+
# Also try the REST API actor endpoint (requires a numeric ID — there is no bare /actors list route)
94+
curl -s -H "Accept: application/activity+json" "SITE_URL/wp-json/activitypub/1.0/actors/1" | python3 -m json.tool
9595
```
9696

9797
- Verify author pages are not blocked (some security plugins block `?author=` enumeration)

0 commit comments

Comments
 (0)