---
name: acpt-integration
description: Activate when working with the ACPT (Advanced Custom Post Types) plugin on a WordPress site — Custom Post Types, Taxonomies, Meta groups (custom fields), or Option Pages. Covers ACPT's three-level field hierarchy (MetaGroup → MetaBox → MetaField), the polymorphic target model for value IO, edit semantics, and the discover→list→get→modify→verify workflow.
---

# ACPT Integration

## When to use

Activate this skill when:
- The user mentions ACPT, acpt.io, or `wp_acpt_*` tables.
- The user asks to create, edit, or delete a custom post type, taxonomy, meta group, or option page on a site that has ACPT active.
- The user asks to read or write field values on a post, term, user, comment, or option page on a site using ACPT.
- `acpt-check-setup` returns `active: true`.

## When NOT to use

- If ACF, Pods, JetEngine, ASE, or Meta Box is the primary content-modeling tool on the site, use the corresponding integration skill instead.
- Always call `acpt-check-setup` first. If `min_satisfied: false`, ACPT is below the supported minimum (2.0.50) — inform the user and stop.
- For a plugin-agnostic representation of the site's content model (snapshots, audits, cross-plugin migrations), activate the `content-model-schema` skill alongside this one.

## Discover before you act

Always start with `novamira/acpt-check-setup`. It returns:

| Field | Meaning |
|---|---|
| `active` | ACPT loaded (`ACPT_PLUGIN_VERSION` defined) |
| `version` | Installed version |
| `min_satisfied` | ACPT >= 2.0.50 |
| `license_active` | ACPT Pro license currently valid — informational only for v1 (all v1 abilities work without Pro) |
| `counts` | `{custom_post_types, taxonomies, meta_groups, option_pages}` |

If `active: false` or `min_satisfied: false`, stop and report. The `license_active` flag is informational: v1 abilities cover the license-agnostic core (CPTs, taxonomies, meta groups, option pages). Premium features (forms, dynamic blocks, datasets, WooCommerce integration) are out of v1 scope.

Then list before getting: call `acpt-list-*` to enumerate existing entities before calling `acpt-get-*` on a specific one.

## The three-level field hierarchy — most important concept

ACPT splits a "field group" into three nested concepts:

- **MetaGroup** — the top-level container (a named collection of meta boxes). Has a name and label, belongs to one or more CPTs / taxonomies / option pages via the `wp_acpt_belong` table.
- **MetaBox** — a sub-container within a MetaGroup (a tab/panel in the editor UI). Has its own visibility rules.
- **MetaField** — the actual input (id, name, type, options, advanced options). The leaf.

Other plugins (ACF, Meta Box, Pods) typically flatten to two levels (group → fields). ACPT's three-level model is exposed faithfully in `acpt-get-field-group-schema include_fields=true`:

```jsonc
{
  "name": "product_details",
  "label": "Product Details",
  "belongs_to": { "type": "post", "id": "shop_product" },
  "meta_boxes": [
    {
      "name": "general",
      "label": "General",
      "fields": [
        { "name": "sku", "type": "text", "label": "SKU" },
        { "name": "price", "type": "number", "label": "Price" }
      ]
    },
    {
      "name": "advanced",
      "label": "Advanced",
      "fields": [
        { "name": "barcode", "type": "text", "label": "Barcode" }
      ]
    }
  ]
}
```

When creating / editing a field group, pass the full hierarchy. The integration applies REPLACE semantics on `meta_boxes` — the entire array is replaced, so always fetch the current state with `acpt-get-field-group-schema include_fields=true` before editing.

## No source awareness

Unlike Meta Box and ACF, ACPT stores **everything in its own DB tables** — there's no PHP/filter-registered fallback. The agent doesn't need to worry about `source: 'php'` rejections; every CPT, taxonomy, meta group, and option page returned by ACPT abilities is editable.

## License gotcha — option pages require Pro

ACPT distinguishes license-agnostic features (CPTs, taxonomies, meta groups) from **Pro-gated** features. **Option pages are Pro-gated** at runtime:

- The `acpt-create-option-page` / `edit` / `delete` / `list` / `get` abilities ALWAYS work — they read and write ACPT's DB rows.
- BUT the **menu page** is only registered with WordPress when both `ACPT_IS_LICENSE_VALID` and `ACPT_ENABLE_PAGES` are true. Without a valid ACPT Pro license, the option page exists in the DB but does not appear in the wp-admin sidebar.

When `check-setup` returns `license_active: false`:
- Tell the user that any option page they create through the abilities will be persisted but invisible in wp-admin until the Pro license is activated.
- Meta values written to those option pages still round-trip correctly via `read-values` / `write-values` (the storage works regardless of license state) — they're just not editable through the admin UI.
- Forms, Dynamic Blocks, Datasets, WooCommerce integration are out of v1 scope anyway, but they're all subject to the same Pro gate.

## Timing — registration is on init, not synchronous

ACPT registers WP-native post types and taxonomies via the `init` hook, not synchronously inside `register_acpt_post_type()` / `register_acpt_taxonomy()`. So:

- An agent that creates a CPT then immediately calls `wp_insert_post` for that CPT slug in the **same request** will get `Invalid post type` — the CPT registration hasn't run yet.
- The agent should split CPT-creation and post-insertion across separate calls / requests, OR call ACPT's lower-level registration manually (advanced).

Same applies to taxonomies + their terms. The PHP smoke test suite handles this by using separate `wp eval-file` invocations for the create / write steps.

## Target model for read-values / write-values

```jsonc
{
  "target": {
    "type": "post|term|user|comment|option-page",
    "id":   123 | "option-page-slug"
  }
}
```

- `post` / `term` / `user` / `comment`: numeric ID
- `option-page`: the menu slug (the integration resolves to internal id)

Values are stored under the MetaField's name in ACPT's `wp_acpt_meta_post`, `wp_acpt_meta_tax`, `wp_acpt_meta_user`, `wp_acpt_meta_comment`, and `wp_acpt_meta_option_page` tables.

## Edit semantics

`acpt-edit-*` abilities use:
- **PATCH** for top-level keys: keys you omit are preserved
- **REPLACE** for arrays (`meta_boxes`, `meta_fields`, `labels`, `supports`, etc.): pass the full desired array

Always fetch the current state with `get-*` before editing if you only want to add or change something — otherwise you might lose siblings.

## Workflow example — modeling a Product CPT

User asks: "Set up a Product CPT with general fields and an advanced tab."

1. `acpt-check-setup` → confirm `active: true, min_satisfied: true`.
2. `acpt-list-post-types` → see what already exists.
3. `acpt-create-post-type` slug=`product`, singular=`Product`, plural=`Products`, public=true.
4. `acpt-get-field-type-schema` for any unfamiliar field types before defining the group.
5. `acpt-create-field-group` with the three-level shape:
   ```jsonc
   {
     "name": "product_details",
     "label": "Product Details",
     "belongs_to": { "type": "post", "id": "product" },
     "meta_boxes": [
       { "name": "general", "label": "General", "fields": [
         { "name": "sku", "type": "text", "label": "SKU" },
         { "name": "price", "type": "number", "label": "Price" }
       ]},
       { "name": "advanced", "label": "Advanced", "fields": [
         { "name": "barcode", "type": "text", "label": "Barcode" }
       ]}
     ]
   }
   ```
6. To populate: `acpt-write-values target={type:'post',id:<product_id>} values={sku:'X1', price:9.99, barcode:'1234'}`.
7. To verify: `acpt-read-values target={type:'post',id:<product_id>} fields=['sku','price','barcode']`.

## Progressive disclosure conventions

- `get-post-type` returns identity + `meta_group_count` by default. Pass `include_meta_groups: true` to inflate.
- `get-field-group-schema` returns `{name, label, belongs_to, meta_box_count, field_count}` by default. Pass `include_fields: true` to inflate the full hierarchy.
- `get-field-type-schema` returns options for ONE field type — call it right before creating/editing a field.

## Recovery from partial failures

WordPress has no transactional rollback. If a multi-step workflow fails midway:
- `acpt-delete-*` abilities are idempotent — re-running `delete-field-group name=product_details` after a partial create returns success cleanly.
- The agent should clean up successfully-created entities, then retry from the failed step.

## Things to NOT do

- Don't bypass ACPT's Repository classes by writing directly to `wp_acpt_*` tables — ACPT enforces invariants and cache flushes through its Repositories.
- Don't use `update_post_meta` directly to write ACPT field values — go through `acpt-write-values` so type-specific serialization (repeaters, files, dates) is handled.
- Don't try to register ACPT-style meta groups via PHP filters — ACPT is DB-first.
- Don't create v2-scoped features (forms, dynamic blocks, datasets, WooCommerce) via the abilities in v1 — they're not exposed yet.

## Things that are safe and encouraged

- Run `acpt-check-setup` at the start of any session involving ACPT.
- Use `get-field-type-schema` whenever building / editing fields — avoids guessing option keys.
- Use `list-field-groups` filtered by `belongs_to` to find groups attached to a specific CPT before editing.
- Prefer `name` selectors over `id` — they're stable and human-readable.
- Combine `create-post-type` + `create-field-group` in a single workflow to set up a content type end-to-end.
