# BW Guides — Specification

**Version:** 0.1.0 | **Last Updated:** 2026-07-23

## Purpose

A guides/documentation system for client WordPress sites with central publishing:

1. **Standard guides** — a library Bowden Works ships to every site running the plugin.
2. **Site-specific guides** — guides targeted at individual sites from the central hub
   (`plugins.bowden.works`, plugin `bw-guides-server`) without logging into the client site.
3. **Client-created guides** — clients write their own guides locally in the block editor.

Clients can **tag** every guide (including BW-shipped ones) and attach private **notes**;
both are local-only and survive syncs. BW-shipped guides are **read-only** on client sites.

## Requirements

### Functional

- R1: Pull-model sync — the hub never pushes. Freshness comes from a background
  update check whenever a Guides screen is opened (throttled: at most once per
  10 minutes, filter `bw_guides_check_interval`), with a "refresh to see the
  latest" banner when something changed (including the guide being read).
  A daily WP-cron run is only a backstop, plus manual Sync Now.
- R2: Per-site key auth (`bwg_<site_id>_<40 hex>`), pasted once into Guides → Settings;
  saving a key triggers an immediate first sync.
- R3: The hub manifest is the complete truth per site — local hub guides absent from a
  successful manifest are trashed (recoverable); re-targeted guides untrash without
  duplicates. Failed syncs change nothing locally.
- R4: Reading UI in wp-admin: list with search + tag filter + source filter, plus a
  single-guide view. Cap `edit_posts` to read/tag/note; `manage_options` for settings.
- R5: Hub guides locked read-only via `map_meta_cap` (even for admins); tags and notes
  saved through plugin-owned admin-ajax endpoints with their own nonce + cap checks.
- R6: Hub content rendered via `do_blocks()` only — never the `the_content` chain — so
  hub content cannot execute client-side shortcodes.
- R7: Client-created guides use the normal block editor and are marked "Created here".
- R8: Uninstall removes settings, cron, and hub guides; keeps client guides and tags.

### Non-functional

- Must work on WordPress 6.0+ with PHP 7.4+
- Must follow WordPress security best practices
- Must be translatable (i18n)
- Must not interfere with other plugins

## Acceptance Criteria

Verified end-to-end 2026-07-23 against the live hub with the sandbox as first client
(see docs/TESTING.md for the runnable steps):

| Requirement | Acceptance | Status |
|---|---|---|
| R1/R2 | Key stored → sync pulls exactly the guides visible to this site (decoy-targeted guide never appears) | ☑ |
| R3 | Untarget → local trash; re-target → same post untrashed; wrong key → error, zero changes | ☑ |
| R4 | Guides menu renders list/single; filters work | ☑ (CLI-level; visual pass pending) |
| R5 | `current_user_can('edit_post')` false for admins on hub guides; tags+notes survive content-update sync | ☑ |
| R6 | Single view renders `do_blocks` output only | ☑ |
| R7 | Local guide keeps `source=local`, stays editable, untouched by repeated syncs | ☑ |
| R8 | uninstall.php reviewed; deletes hub guides only | ☑ (code path; not fire-tested) |

## Deferred (payload `meta` object already carries the hooks)

- Variable tokens (`{{admin_url}}` etc.) — replace client-side at render time only.
- Per-guide WP-version metadata + mismatch notice (`meta.min_wp_version`).
- Plugin-conditional visibility (`meta.requires_plugins`) — filter at query time.
- Frontend display option; image sideloading (images currently hotlink from the hub).

## Counterpart

Hub plugin: `/srv/apps/bw-plugins-dist/wp-content/plugins/bw-guides-server/` — its
CLAUDE.md documents the REST contract (`bw-guides/v1`) and the sites registry.
