=== Tessera for the Abilities API ===
Contributors: ibrahimhajjaj
Tags: abilities-api, mcp, audit, rollback, safety
Requires at least: 6.9
Tested up to: 6.9
Requires PHP: 8.1
Stable tag: 1.3.5
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Snapshot, audit, and rollback layer for plugins that register abilities via the WordPress Abilities API.

== Description ==

Tessera is a developer library for plugin authors who register abilities via `wp_register_ability()` and want snapshot capture, audit logging, approval workflows, and one-click rollback for every invocation across REST, MCP, internal PHP, and WP-CLI without building it themselves.

Declare what state your ability touches; Tessera handles the safety wrapper.

= What you get out of the box =

* **Pre + post snapshots.** Every safety-enabled invocation captures declared state before the callback and (on success) after, so the audit log can show a real diff.
* **Audit log.** One row per invocation with ability name, caller (REST/MCP/CLI/internal), user, args, result, status, duration, pre/post hashes, and parent_invocation_id for nested calls.
* **One-click rollback.** Restore captured state from post_meta, options, taxonomy term assignments, user roles + caps. File contents support tiered drift detection (mtime / mtime_size / critical_hash / full_hash) plus opt-in real byte-level rollback via `full_content` strategy.
* **Drift check on rollback.** Live state is hashed and compared to the snapshot's post-state before restoring; if they differ the rollback returns an error unless forced.
* **Concurrency lock.** Capture + execute is serialised per surface set via a MySQL advisory lock so two simultaneous invocations do not capture each other's mid-states.
* **Encrypted redaction.** Scrub secrets out of args, results, and snapshots. Stores redacted values as AES-256-GCM envelopes so rollback can still restore them.
* **Approval queue.** When `safety.requires_approval` is set, the wrapper blocks execution and returns a 202 pending response. A human approves or rejects via wp-admin, WP-CLI, or REST. Multi-stage sequential or parallel approval chains are supported.
* **Multisite support.** Each subsite gets its own set of `wp_<N>_abilityguard_*` tables, with auto-install on `wp_initialize_site` and auto-drop on `wpmu_drop_tables`.
* **Retention.** Daily WP-Cron prunes old log rows (defaults: 30 days normal, 180 days destructive) and orphaned snapshots.

= Surfaces =

* **PHP API** with `wp_register_ability( $name, [ ..., 'safety' => [...] ] )` and helpers `abilityguard_rollback`, `abilityguard_snapshot_meta`, `abilityguard_snapshot_options`.
* **REST**: `/abilityguard/v1/log`, `/log/<id>`, `/log/export`, `/rollback/<id>`, `/rollback/bulk`, `/approval`, `/approval/<id>/approve`, `/approval/<id>/reject`, `/approval/bulk`, `/approval/export`, `/retention`, `/retention/prune`, `/health`.
* **WP-CLI**: `wp abilityguard log list/show`, `wp abilityguard rollback <id>`, `wp abilityguard approval list/approve/reject <id>`, `wp abilityguard prune`.
* **wp-admin**: Tools > Tessera. Hybrid timeline + command-palette search, snapshot drawer, JSON-highlighted Input/Result tabs, invocation chain navigation, and real rollback against the captured snapshot.

= Example =

`
wp_register_ability( 'my-plugin/update-product-price', array(
    'label'               => 'Update product price',
    'description'         => 'Updates the price on a WooCommerce product.',
    'category'            => 'woocommerce',
    'input_schema'        => array( /* ... */ ),
    'permission_callback' => fn() => current_user_can( 'manage_woocommerce' ),
    'execute_callback'    => fn( $args ) => update_post_meta( $args['product_id'], '_price', $args['price'] ),
    'safety' => array(
        'destructive'       => true,
        'requires_approval' => false,
        'snapshot'          => fn( $input ) => array(
            'post_meta' => array( $input['product_id'] => array( '_price', '_regular_price' ) ),
            'options'   => array( 'woocommerce_last_price_change' ),
        ),
    ),
) );
`

= Documentation =

Full plugin-author documentation lives at the GitHub repo: https://github.com/ibrahimhajjaj/abilityguard

== Source Code ==

The full source for Tessera, including the unminified React source for the admin app, lives on GitHub: https://github.com/ibrahimhajjaj/abilityguard

* The admin bundle `assets/admin.js` is compiled from `assets/admin.jsx` (React + JSX, no preprocessor magic beyond JSX).
* The bundler is [esbuild](https://esbuild.github.io/), configured in `scripts/build.mjs`.
* To rebuild the admin bundle from a fresh checkout, run `npm install` once, then `npm run build` whenever `assets/admin.jsx` changes. This regenerates `assets/admin.js` in place.
* The release zip published to the WordPress.org directory is produced by `scripts/build-release.sh`, which excludes development artifacts (tests, examples, build configs) but keeps everything required for the plugin to run.

== Installation ==

1. Upload the `abilityguard-mcp` folder to `/wp-content/plugins/`.
2. Activate the plugin through the Plugins menu in WordPress (or network-activate on multisite).
3. Visit Tools > Tessera to view the audit log.
4. In your own plugin, register abilities via `wp_register_ability()` with a `safety` config.

Requires WordPress 6.9 or later (for the Abilities API) and PHP 8.1 or later.

== Frequently Asked Questions ==

= Does this work without other plugins? =

It will activate without registered abilities, but it only does work when other plugins register abilities with a `safety` config via `wp_register_ability()`.

= What state surfaces are supported for snapshots? =

post_meta, options, taxonomy term assignments, user roles + caps, and files (with five tiered strategies from mtime to full content rollback).

= Does it support multisite? =

Yes. Each subsite gets its own set of `wp_<N>_abilityguard_*` tables. New subsites are auto-installed via `wp_initialize_site`; deleted subsites have their tables dropped via `wpmu_drop_tables`.

= How does it handle concurrent invocations? =

Per-surface MySQL advisory locks (GET_LOCK) serialise capture + execute so simultaneous invocations do not capture each other's mid-states.

= Are secrets encrypted in the log? =

Yes. Redaction uses AES-256-GCM envelopes so rollback can still restore the original value when the encryption key is intact.

== Screenshots ==

1. Invocation timeline. Every ability call across REST, MCP, internal PHP, and WP-CLI, with caller attribution and per-row status.
2. Approvals queue. Pending requests waiting on a human, with the requesting context and a one-click approve or reject.
3. Invocation detail after a one-click rollback restored the captured pre-state.
4. Search-as-you-type in the log: ability name, caller, status.
5. Invocation detail, result tab, with redacted secret values restored on display when the encryption key is present.
6. Snapshot drawer showing the captured pre-state and post-state for a destructive invocation.
7. Multi-stage approval chain with per-stage capability and role routing.

== Changelog ==

= 1.3.5 =
* Release zip no longer ships `composer/installers` and its unused installer adapters. Cuts the published zip from 909K to 824K and 206 files to 98.

= 1.3.4 =
* Release zip now ships `vendor/autoload.php` so the plugin actually boots on a fresh install. (1.3.3 zip was missing the autoloader and fatal'd on activation.)

= 1.3.3 =
* Display name changed to "Tessera for the Abilities API" to clearly distinguish this plugin from any future official safety library. Slug, text domain, and internal namespace are unchanged.
* `error_log()` calls in the rate-limiter and concurrency lock are now gated behind `WP_DEBUG`, so production hosts no longer accumulate noise from fail-open paths.
* `readme.txt` gains a Source Code section documenting the GitHub repository, the esbuild-based build pipeline, and the `npm run build` command used to regenerate `assets/admin.js`.

= 1.3.2 =
* Slug renamed to `abilityguard-mcp` for the WordPress.org directory.
* Snapshot file blobs now stored under `wp-uploads/abilityguard-mcp/` instead of `wp-content/abilityguard-staging/`.
* Admin page CSS folded into the existing enqueued bundle; no more inline `<script>`/`<style>` echoes.
* `$_SERVER['REMOTE_ADDR']` is unslashed and sanitized before being hashed for IP-keyed rate-limit principals.

= 1.3.1 =
* Skipped (broken release-workflow build).

= 1.3.0 =
* Sliding-window-counter rate limiter with multi-policy support (burst + sustained), pluggable storage (Redis / object cache / transient), and IETF draft RateLimit headers.
* Dry-run mode: per-call `safety.dry_run` previews a destructive ability, persists the diff, auto-rolls-back, and surfaces details via `/dry-run/<id>` REST endpoint and `abilityguard_get_dry_run_result()` helper. Result returns untouched so it validates against `output_schema`.
* Approval queue gains per-stage role routing (`approval_roles`) and separation-of-duties enforcement across the chain.
* Per-status retention via `abilityguard_retention_days_by_status`.
* `/stats` REST endpoint and admin dashboard widget (counts, p50/p95, top abilities).
* Wrapper split into observability listeners on `wp_before_execute_ability` / `wp_after_execute_ability` plus an enforcement seam (`abilityguard_pre_execute_decision` filter) for plugin extensions.
* Reads `meta.annotations.destructive` directly from core (WP 6.9 surface), no parallel safety metadata.
* Requires WP 6.9; pre-6.9 fallback path removed.

= 1.2.0 =
* Parallel multi-stage approval chains with optional per-stage user pinning.
* `/health` REST endpoint and a pending-approvals badge in the admin bar.
* WP-CLI: `log show --diff`, `approval show`, `prune --all-sites`.
* JSONL export option for audit log.
* Real byte-level file rollback via `safety.snapshot.files.strategy = 'full_content'` (AES-256-GCM, content-addressed sidecar staging dir, atomic writes, 256 KB per-file cap).
* Full multisite support with auto-install on subsite creation and auto-drop on subsite deletion.
* Sequential and parallel multi-stage approval chains.

= 1.1.0 =
* Multi-stage approval queues.
* Invocation correlation via `parent_invocation_id` and an admin-side invocation chain navigator.
* `log_meta` table for extensible per-row metadata.

= 1.0.0 =
* Initial public release.
* Snapshot, audit, rollback, and approval middleware for the WordPress Abilities API.
* Five collectors: post_meta, options, taxonomy, user_role, files.
* REST + WP-CLI + wp-admin surfaces.
* Encrypted redaction, payload caps, retention pruning.
